MathanMV
MathanMV

Reputation: 412

Uncaught TypeError: <function> is not a function

I am pretty new to Javascript and I've across an issue I can't seem to understand. The console is complaining saying it is not recognising the function.

I have the following code (GraphActions.js):

var VentureDispatcher = require('../dispatcher/VentureDispatcher');
var GraphStoreConstants = require('../constants/GraphStoreConstants');
var StockService = require('../utils/StockService');

var GraphActions = {};

  GraphActions.getGraphData = function(request){
    //have the API call here.
    VentureDispatcher.handleViewAction({
      actionType: GraphStoreConstants.GET_GRAPH_DATA,
      data: request
    });
    StockService.getHistoricStockData(request);
  };

  GraphActions.recieveGraphData = function(response){
    VentureDispatcher.handleServerAction({
      actionType: GraphStoreConstants.GRAPH_DATA_RESPONSE,
      response: response
    });
  };

  GraphActions.test = function(){
    console.debug("found this.");
  };

module.exports = GraphActions;

And the following Javascript file which is calling the function within the code above:

var request = require('superagent');
var GraphActions = require('../actions/GraphActions');

var StockService = {

  getHistoricStockData: function(symbol){
      request.post('http://localhost:8080/historicalData')
             .send({
                        "symbol":"GOOG",
                        "from":"2016-06-01",
                        "to":"2016-07-01"
                    })
             .set('Content-Type','application/json')
             .end(function(err,res){
               if(err || !res.ok){
                  console.error('Error making request!');
               }else {
                  console.debug(JSON.stringify(res.body));
                  GraphActions.recieveGraphData(res.body);
              }
            });
  }
};

module.exports = StockService;

The console is throwing the following error and not too sure why: venture-stock.js:44673 Uncaught TypeError: GraphActions.recieveGraphData is not a function.

Does anyone understand why this is happening? The same method has been called in another place with no problem.

When debugging the code and evaluating GraphAction object in the above code I get the following where the functions defined above are not available:

enter image description here

Where as in another location, the functions are available:

enter image description here

Any help would be appreciated!

Upvotes: 1

Views: 5766

Answers (1)

MaxArt
MaxArt

Reputation: 22637

It happens because of a circular reference among your modules. The module GraphActions requires the module StockService, which also requires GraphActions. It doesn't matter if what's actually needed in StockService it's the recieveGraphData method, while the method that requires StockService is getGraphData: the module loader doesn't have this level of code analysis. require would always return the whole module, so it's a circular reference.

So, what happens in these cases?

The module loader, when loading GraphActions, meets require('../utils/StockService'), then stops the execution of GraphActions in order to load StockService. At this point, the exported properties of GraphActions are... none. So that's why in StockService you get an empty object.

Solution A

Merge the two modules in one, e.g.

var GraphService = {
    getGraphData: function(request) {
        ...
        GraphService.getHistoricStockData(request);
    },
    recieveGraphData: function(response) { ... },
    getHistoricStockData: function(symbol) {
        ...
        GraphService.recieveGraphData(res.body);
    }
};
module.exports = GraphService;

Solution B

It's the opposite, i.e. decouple getGraphData and recieveGraphData in two different modules. I don't really like this one, because it can lead to excessive module fragmentation.

Solution C (recommended)

As long as CommonJS is used, you can take advantage of using require wherever you want, so in StockService.js you'd do:

getHistoricStockData: function(symbol) {
    request.post('http://localhost:8080/historicalData')
        ...
        .end(function(err, res) {
            ...
            var GraphActions = require('../actions/GraphActions');
            GraphActions.recieveGraphData(res.body);
        });
}

Now, since the execution will not immediately require GraphActions, the module will be loaded later when all its dependencies are fully loaded, so you'll get a fully working module.

Differences with ES2015 modules

In ES6, you cannot use import outside the root level, i.e. you can't use import inside a function. So you couldn't use solution C with ES6 syntax?

Actually that wouldn't be necessary to begin with, because CommonJS module resolution is different than ES6's, the latter being more advanced. So, you'd have written:

import * as StockService from '../utils/StockService';
...
export function getGraphData(request) {
    ...
    StockService.getHistoricStockData(request);
};
export function recieveGraphData(response) { ... };
export function test() { ... };

and in StockService.js:

import * as GraphActions from '../actions/GraphActions';
...
export function getHistoricStockData(symbol) {
    request.post('http://localhost:8080/historicalData')
        .send( ... )
        .set('Content-Type','application/json')
        .end(function(err, res) {
             ...
             GraphActions.recieveGraphData(res.body);
        });
};

So what happens here? GraphActions.js is loaded, the import statement is reached, the execution of the module stops and then StockService is loaded. So far, it's the same as CommonJS.

In StockService, the loader would meet the import statement, then the execution of GraphActions resumes and the whole module is correctly exported. So you'd be golden from the start with ES6.

This will lead us to

Solution D (most recommended... if you can)

Since you're already using Babel, use the plugin "transform-es2015-modules-commonjs" or "preset-es2015" and use ES6 modules instead.

Hope this cleared your doubts!

(Btw, it's "receive", not "recieve" ;)

Upvotes: 7

Related Questions