Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uncaught TypeError: model.invalidateClassCache is not a function #29

Closed
gCardinal opened this issue Aug 8, 2016 · 22 comments
Closed

Uncaught TypeError: model.invalidateClassCache is not a function #29

gCardinal opened this issue Aug 8, 2016 · 22 comments

Comments

@gCardinal
Copy link

When following the examples in the documentation, I constantly get this error. It happens in the Schema, in the register method. When looping through the models being registered, the model's class doesn't have a invalidateClassCache method.

It's not impossible the problem is on my side, but there's one detail that is currently making me believe there is something more at play here: I have access to most, if not all, of the properties an object extending Model should have. I'm talking about things like querySetClass and virtualFields. I also seem to have a valid object when I use Chrome's console and break right before the error and instantiate the current model in the loop.

I should also mention I'm using TypeScript, but this should not affect anything in the use of this library. Especially since you're using ES6. Note that even though my code right now is not following exactly the syntax of your example, it does compile to the equivalent and I did try following your exact syntax since TypeScript really doesn't mind if I use pure JS.

That being said, here's the code that's causing this error:

// Book.ts
import { fk, Model } from 'redux-orm';

export default class Book extends Model {
    public static modelName: string = 'Book';
    public static fields: any = {
        author: fk('Author'),
    };
}

// public static properties compile to Book.modelName and such
// Schema.ts
import { Schema } from 'redux-orm';
import Book from 'models/Book';

const schema = new Schema();
schema.register(Book);

export default schema;
// RootReducer.ts
import { combineReducers } from 'redux';
import Schema from 'models/Schema';

export default combineReducers({
    orm: Schema.reducer(),
});
@tommikaikkonen
Copy link
Collaborator

That's weird, your code looks correct. Is your build system resolving import Book from 'models/Book' to the correct file? Without adding custom resolve paths in webpack, to get a file relative to the directory of the current file I need to use the relative import ./models/Book. If the import is resolving to undefined or an empty object, that would explain the exception.

@gCardinal
Copy link
Author

gCardinal commented Aug 15, 2016

I'm glad to see I'm not the only one who thinks my code is fine! I'm not
crazy after all!

It is resolving properly. I have setup Webpack to resolve paths so that import
Book from 'models/Book' will work. I'll copy my webpack configuration over
tomorrow. I also confirmed via the console that the file gets properly
resolved.

I'm not too sure when I'll have time to keep working on integrating
redux-orm into my project, but I'll keep this issue updated with anything I
find.

@gCardinal
Copy link
Author

I still really doubt that's the problem, but still, here's my webpack configuration. I want to find some more time to get redux-orm working this week. I will update this with anything I can figure out.

var path = require('path');
var webpack = require('webpack');
var root = __dirname + '/../../';

module.exports = {
    output: {
        path: path.join(root, 'dist/assets'),
        filename: 'master.js',
        publicPath: '/assets/'
    },
    entry: [
        'webpack-dev-server/client?http://0.0.0.0:3000',
        'webpack/hot/only-dev-server',
        'babel-polyfill',
        'whatwg-fetch',
        path.join(root, 'src/ts/index.tsx'),
        path.join(root, 'src/sass/screen.scss')
    ],
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /fr|en/),
        new webpack.IgnorePlugin(/^fs$/),
    ],
    module: {
        loaders: [{
            test: /\.scss|\.sass|\.css$/,
            loaders: [
                'style?sourceMap',
                'css?-url&modules&importLoaders=1&localIdentName=[name].[local].[hash:base64:5]',
                'resolve-url',
                'sass?sourceMap'
            ],
        }, {
            test: /\.tsx?$/,
            exclude: /node_modules|moment\/locale/,
            loaders: ['react-hot', 'ts-loader']
        }, {
            test: /\.json$/,
            loader: 'json',
        }, {
            test: /sinon.js$/,
            loader: "imports?define=>false"
        }],
        noParse: [
            /\/sinon\.js/,
        ]
    },
    devServer: {
        port: 3000,
        ip: "0.0.0.0"
    },
    ts: {
        transpileOnly: true,
        compilerOptions: {
            declaration: false,
        }
    },
    sassLoader: {
        includePaths: [
            path.resolve(root, "src/sass"),
            path.resolve(root, "src/ts/components"),
            path.resolve(root, "node_modules/bourbon/app/assets/stylesheets"),
        ]
    },
    resolve: {
        extensions: ['', '.js', '.ts', '.tsx', '.json'],
            root: [
            path.join(root, 'src/ts'),
            path.join(root, 'tests')
        ],
        alias: {
            sinon: 'sinon/pkg/sinon',
            react: path.resolve('./node_modules/react')
        }
    },
    devtool: 'eval',
    externals: {
        'react/lib/ExecutionEnvironment': true,
        'react/lib/ReactContext': true,
        'react/addons': true,
        'jsdom': 'window',
        'text-encoding': 'window',
    }
};

@kfrijters
Copy link

Did you manage to get it working @gCardinal ? I'm having the same issue with typescript.

@gCardinal
Copy link
Author

No, I gave it a few more hours and couldn't get it working. I sadly had to drop redux-orm because of it.

I switched to using reselect and normalizr to flatten my state and then use selectors to compose that flat data into objects my code will use.

@kfrijters
Copy link

I've given it a couple of hours as well... unfortunately without any success.

@Aryk
Copy link

Aryk commented Dec 13, 2016

Hey @gCardinal and @m1nd53t, I'm also using typescript and having the same issues. Will see if I can find a solution.

@Aryk
Copy link

Aryk commented Dec 13, 2016

Hi all, I've made some progress on this. This has to do with the way the library is compiled and how typescript is interpreting that compilation.

Essentially:

const Model = class Model {
  static foo() {
    return "car";
  }  
}
Model.bar = function() {
  return "far"
}

// In your TS code:
class User extends Model {
}
console.log(Model.foo()) // => "car"
console.log(User.foo()) // => ERROR!
console.log(Model.bar()) // => "far"
console.log(User.bar()) // => "far"

So, there are two solutions:

  1. Either rewrite all the static methods in this format:
Model.staticMethod = function() {
  return "Yay, I work!";
}

or

  1. Find out why the transpiling of the program (or typescript) is not playing nice.

Some Notes: When TS translates a normal class extension, it will look something like this:

const A = class A {
  static foo() {
    return "d";
  }
}
class B extends A {
}

// becomes....

	var A = (function () {
	    function A() {
	    }
	    A.foo = function () {
	        return "d";
	    };
	    return A;
	}());
	var B = (function (_super) {
	    __extends(B, _super);
	    function B() {
	        return _super.apply(this, arguments) || this;
	    }
	    return B;
	}(A));
	console.log(B.foo());

However, when it extends from Redux-ORM, it does not redefine the methods because when typescript enumerates on the object's properties, it cannot find it because the static methods are not enumerable:

microsoft/TypeScript#12059

Anyone have any suggestions on how we can move forward? Would be curious to find other libraries that define static methods that work with Typescript and how they get it to work.

Has anyone with Typescript managed to get this to work? If so, how?

@Aryk
Copy link

Aryk commented Dec 13, 2016

Ok, I solved it! All you have to do is add a plugin to your .babelrc:

{
    "presets": ["es2015", "stage-2"],
    "plugins": [
      "transform-runtime",
      ["transform-es2015-classes", {
        "loose": true
      }]
    ]
}

and to your package.json under devPackages:

    "babel-plugin-transform-es2015-classes": "6.18.0",

transform-es2015-classes changes the static methods to default to Enumerable so that when TS is iterating over the functions on the class-level, it is finding them.

@tommikaikkonen - can you merge this in?

@tommikaikkonen
Copy link
Collaborator

Awesome that you got to the bottom of this @Aryk! This will help a lot of people.

Looking at this, the loose mode shouldn't affect Redux-ORM internals nor users so it should be ok to use that transform when compiling the library. I'll add that in. I encourage you to file an issue about this to TypeScript because it seems like they're not supporting the ES6 spec, which is the real culprit here.

@Aryk
Copy link

Aryk commented Dec 14, 2016

Great, it's already filed here. I just popped in and asked when it might get fixed.

@kfrijters
Copy link

Thanks @Aryk !

@Aryk
Copy link

Aryk commented Dec 17, 2016

know you are probably busy, just curious when you think this will get merged in and released? Would it be helpful if I made a PR?

Also, btw, where is the branch of the new 0.9 code?

@tommikaikkonen
Copy link
Collaborator

Sorry for the delay! I applied the changes to .babelrc and package.json per your instructions, and added a test to check that the Model static methods are enumerable. 0.9.0-rc.1 has these fixes.

@tommikaikkonen
Copy link
Collaborator

The 0.9 code is in feature/eager_updates, master is still at 0.8.

@edaloicaro18
Copy link

Even though I'm already using the plugin that @Aryk recommended, I still get this error message:
"TypeError: model.invalidateClassCache is not a function"

This is my my babel configuration in package.json

{
"babel": {
    "presets": [
      "react",
      "env"
    ],
    "env": {
      "development": {
        "presets": [
          "env",
          "react-hmre"
        ],
        "plugins": [
          "transform-class-properties",
          "transform-object-rest-spread",
          [
            "transform-es2015-classes",
            {
              "loose": true
            }
          ]
        ]
      }  
    }
  }
}

Do you notice anything wrong?

@Aryk
Copy link

Aryk commented Aug 31, 2017

what version are you using of redux-orm?

@edaloicaro18
Copy link

Hi, @Aryk . Version 0.9.4. I think it's the most recent version, right?
redux orm

@Aryk
Copy link

Aryk commented Sep 1, 2017

yeah, i think that is right, my advice would be to

  1. try from the https://github.com/tommikaikkonen/redux-orm/tree/feature/eager_updates branch. If that still doesn't work, then it is something on your end.
  2. Also you can check your node_models directory for the compiled code and read it, see how the files in the /redux-orm/lib folder are compiled when it comes to static methods. There are a few static methods in there, they should be compiled in the "transform-es2015-classes", { "loose": true } style

I know reading the compiled code is a PITA, but knowing what is actually being run is the most likely way to figure out why it's not working. 😄

@chuenlok
Copy link

@edaloicaro18
I have the same problem and I found that I missed export default <model name> while importing things. Can you check if this is your case?

@tijwelch
Copy link

Thanks @chuenlok. Fixed it for me.

@OlympusatDevelopment
Copy link

@chuenlok Fixed it for me too. I was forgetting to add default to my export of a class extending Model.

haveyaseen pushed a commit that referenced this issue Jun 6, 2018
* Add default export for Book to Readme 

To avoid forgetting this when starting out with the library - see also #29

* ;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants