Reputation: 412
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:
Where as in another location, the functions are available:
Any help would be appreciated!
Upvotes: 1
Views: 5766
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.
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;
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.
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.
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
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