Please see demo here.
There are two select, act as filter. Changing main select will change the sub select. When either of them changed, I will send their values to the server, and get the async result.
The problem is when or where do I make the async call. In the demo above, I make async call when I change main select or sub select.
onMainSelectChange: function(value) {
this.setState({
mainSelectValue: value
});
this.onSubSelectChange(this.options[value][0]);
this.getAsyncResult();
},
Because setState is async, and I need to send the latest state to the server, I do this in getAsyncResult
getAsyncResult: function() {
var self = this;
// wait for setState over, so we can get the latest state
setTimeout(function() {
var params = self.state.mainSelectValue + self.state.subSelectValue;
// send params to server, get result
setTimeout(function() {
self.setState({
asyncResult: params + Date.now()
});
}, 300);
}, 20);
},
Using setTimeout here feels hacky to me. I wonder if flux can help improve this case.
Well, the signature of the method setState is actually :
void setState(
function|object nextState,
[function callback]
)
There is an optional callback parameter that will be called when state is set. So you can totally use it to trigger your request to the server. So your piece of code would look like that :
onMainSelectChange: function(value) {
this.setState({
mainSelectValue: value
}, this.getAsyncResult.bind(this));
this.onSubSelectChange(this.options[value][0]);
}
And you can then remove your first setTimeout call
Related
I get my initial data from an outside JSON in
componentDidMount: function() { .... $.get(jsonfile, function(data) { ....
But the state of "jsonfile" changes via an input.
When the state of "jsonfile" prop changes the render method is invoked again, but I will also want to re-run the $.get request.
What would be the proper (react) way to do it?
You should abstract away your data fetching. If you put your fetching of data in a separate helper method you can call that method when needed, and it should do the fetching (and later updating) of the state
React.createClass({
componentDidMount: function () {
this.fetchData();
},
fetchData: function () {
var _this = this;
$.get('....', function (result) {
_this.setState(result);
});
},
handleClick: function () {
this.fetchData();
},
render: function () {
return (<div onClick={this.handleClick}>{data}</div>);
},
});
Please do upload some code from your project if you can.
From what I understand, you are calling an API to produce a JSON response and you have a user input to the same json?
In that case if you want to make supplementary calls to the API, you should place your API call in the correct Lifecycle Methods provided by a react component.
Please see https://facebook.github.io/react/docs/component-specs.html
I am learn reactjs flux pattern from the link below
https://scotch.io/tutorials/getting-to-know-flux-the-react-js-architecture
I get completely confused and lost with this following bit, when will the following code ever been trigger or used? i do not see any where in the app where the this._onChange will be trigger... please help and explain any suggestion is appreciated as i am start to learn.
// Listen for changes
componentDidMount: function() {
ShoeStore.addChangeListener(this._onChange);
},
// Unbind change listener
componentWillUnmount: function() {
ShoesStore.removeChangeListener(this._onChange);
},
in the store, does it means in order to trigger the update need to run ShoesStore.emitChange()?
// Emit Change event
emitChange: function() {
this.emit('change');
},
// Add change listener
addChangeListener: function(callback) {
this.on('change', callback);
},
// Remove change listener
removeChangeListener: function(callback) {
this.removeListener('change', callback);
}
In typical Flux app, your store emit change (as EventEmitter) and _onChange is invoked because it has been assigned by addChangeListner, which needs to be removed afterwards otherwise it cause memory leak. componentDidMount and componentWillUnmount is invoked at certain life cycle phases, as method names say, just after the component is mounted, and just before unmounted.
Correct answer: (summary from BinaryMuse)
When you're creating a store you'll usually call emitChange() yourself (it's not usually automatic).
So, I have two stores. First pageStore serves business logic of specific page, and second globalStore logic of Android/iOS global events.
When user enters specific page React.componentDidMount calls
pageEntered: function () {
this.listenTo(globalStore, this.locationUpdated);
},
so from this my pageStore started to listen global storage for GPS updates. But is there any way to disconnect listenTo on React.componentWillUnmount ?
There's an example how to unsubscribe from a store listening (taken from the official examples):
var Status = React.createClass({
getInitialState: function() { },
onStatusChange: function(status) {
this.setState({
currentStatus: status
});
},
componentDidMount: function() {
this.unsubscribe = statusStore.listen(this.onStatusChange);
},
componentWillUnmount: function() {
this.unsubscribe();
},
render: function() {
// render specifics
}
});
Here's one way to think about what's happening in the example above:
var myFunc = function(){
console.log("This gets fired immediately");
var randomNumber = Math.ceil(Math.random()*10);
return function() {
return randomNumber;
}
}
var a = myFunc(); //Console log fires IMMEDIATELY, a is the returned function
a(); //some integer from 1 to 10
Since myFunc is invoked when we assign it to a variable, the console.log fires immediately-- it is like this.unsubscribe = statusStore.listen(this.onStatusChange); which "turns on" the listener immediately once componentDidMount happens.
In the componentDidMount lifecycle method, we are attaching a listener to using .listen. That's invoked. Just for convenience, we are assigning the result of the function to this.unsubscribe.
If you look at lines 60-68 of this gist (https://gist.github.com/spoike/ba561727a3f133b942dc#file-reflux-js-L60-L68) think of .listen returning a function that that removes the event listener.
In componentWillUnmount, we invoke this.unsubscribe which removes the listener. You can think of .listen as returning a function that removes the 'listener' and when the componentWillUnmount lifecycle happens we invoke that function and kill the listener.
Tl;dr: Imagine .listen attaches a listener AND returns a function that turns off the listener-- when you first invoke it the listener on and when you invoke the function that gets returned it turns off the listener
I load my store as follows:
store.load({
params: {
paramMap
},
callback: function(records, options, success) {
if (success) {
var form = formPanel.getForm();
var jsonStr = Ext.JSON.encode(records[0].raw);
var jsonObj = Ext.JSON.decode(jsonStr);
form.loadRecord(jsonObj);
}
}
});
The thing is I only want this call back to fire the first time the store is loaded. I want to remove it after that so that when I reload or load the store again it doesn't call this callback again.
Any idea, I tried getting the callback in the options config but that doesn't seem to work.
Not exactly sure what the problem is. The callback will only get called when the store is loaded with the callback.
store.load({callback:myCallback});
//callback will be called
store.load();
//callback will not be called
If you want to just do something once you might want to use a listener with single set to true. Listeners instead of callbacks is my preferred way.
store.on('load', onStoreLoad, this, {single:true});
The callback function arguments api is slightly different than a load listener, check the docs to see.
I am looking for the best way to trigger an event when the fetch is completed.
This code works but I am curious to know if there is a better way to accomplish the following task.
var myApp = new Marionette.Application();
myApp.on("initialize:before", function () {
this.currentUser = new UserModel();
this.currentUser.fetch({
complete: function () {
myApp.vent.trigger('currentUser');
}
});
});
A successful fetch triggers a "change" event:
fetch model.fetch([options])
[...] A "change" event will be triggered if the server's state differs from the current attributes.
So if the fetch does anything, there will be a "change" that you can listen for:
myApp.on("initialize:before", function () {
this.currentUser = new UserModel();
this.currentUser.on('change', function() {
myApp.vent.trigger('currentUser');
});
this.currentUser.fetch();
});
That will also trigger a "currentUser" event if this.currentUser is changed in some other way and that might or might not be what you want. If you only want your function called once then your current success handler is probably the easiest thing to do; you could still use on and unbind the handler when it is called:
myApp.on("initialize:before", function () {
var triggerCurrentUser = _.bind(function() {
myApp.vent.trigger('currentUser');
this.currentUser.off('change', triggerCurrentUser);
}, this);
this.currentUser = new UserModel();
this.currentUser.on('change', triggerCurrentUser);
this.currentUser.fetch();
});
You could also use _.once but that would leave a no-op callback function kicking around and there's no need for that.