Here is a summary of the code I have inside my React component:
getInitialState: function(){
return{link:""}
},
onClick1: function(){
this.setState({link:"Link1"});
this.otherFunction();
},
onClick2: function(){
this.setState({link:"Link2"});
this.otherFunction();
},
otherFunction:function(){
//API call to this.state.link
},
render: function(){
return <div>
<button1 onClick={this.onClick1}>Link1</button>
<button2 onClick={this.onClick2}>Link2</button>
//...some code to display the results of API call
</div>
}
The problem I have is that the first time I click the button, the otherFunction will run, but it will not have the updated value of myState. If I click a second time, then it works correctly.
From the docs:
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.
If you want a function to execute after the state transition completes, pass it in as a callback:
onClick1: function() {
this.setState({link:"Link1"}, this.otherFunction);
},
Well, here I am answering my own question, for future reference.
I figured it out. I removed this.otherFunction() from the onClick functions, and put it in componentWillUpdate. So it looks like this:
getInitialState: function(){
return{link:""}
},
onClick1: function(){
this.setState({link:"Link1"});
},
onClick2: function(){
this.setState({link:"Link2"});
},
otherFunction:function(){
//API call to this.state.link
},
componentWillUpdate(){
this.otherFunction();
},
render: function(){
return <div>
<button1 onClick={this.onClick1}>Link1</button>
<button2 onClick={this.onClick2}>Link2</button>
//...some code to display the results of API call
</div>
}
If all that onClick does is change the state, then you shouldn't have two functions that do the same job. You should have the new value of the "link" state passed as an argument to the function "onClick" :)
Related
Consider a component in React which has its own state and also uses some shared state (shared states are stored within a Store).
Here's a sample component for better understanding the question:
var ControllerView = React.createClass({
getInitialState: function() {
return {privateState: 1, sharedState: -1};
},
componentDidMount: function() {
Store.addChangeListener(this._onChange);
},
componentWillUnmount: function() {
Store.removeChangeListener(this._onChange);
},
_onChange: function() {
this.setState({
privateState: this.state.privateState,
sharedState: Store.getSharedState()
});
}
stepForward: function() {
this.setState({
privateState: this.state.privateState + 1,
sharedState: this.state.sharedState
});
Action.decrease();
},
render: function() {
return (
<div>
<button onClick={this.stepForward}>Go forth with private and shared state</button>
<div>Private State: {this.state.privateState}</div>
<div>Shared State: {this.state.sharedState}</div>
</div>
);
}
});
As you can see in the given code, there's a button which pressing it will result in changing both private state and shared state. A private state can simply be updated by calling the setState method. But following the Flux architecture, updating a store should go through actions. That's why there's a Action.decrease();.
Just a recap of what will happen when an action is called;
The action method called will come up with the new data (either through calculation or calling a service, in my question it doesn't matter how you come up with the new data).
Once the action has got the new data, it will dispatch it so any store interested in it and they will pick it up and store it.
After any store saves the data, it will emit a change notifying any component registered within that store to pick up the new data. This is when the component's _onChange is called.
So when the button is pressed, the component's state will update twice, once to update the private state and the second time when _onChange is called to update the shared state. These two setState calls happen one after the other in one thread.
What I experienced is that in such cases only the last setState will apply and the previous ones are discarded. My question is how to overcome this problem?
When calling setState, you should only include the properties you are actually updating - other properties in the state will be left alone. You are seeing the fact that, when setState is called, this.state is not immediately updated, but only once the actual react render starts (immediately after componentWillUpdate() and before render() I think). So when you call your second setState, you are replacing the new value for privateState with the old one (which is still held in this.state until the render happens). So, your code should look like:
_onChange: function() {
this.setState({
sharedState: Store.getSharedState()
});
}
stepForward: function() {
this.setState({
privateState: this.state.privateState + 1,
});
Action.decrease();
}
Looking at Facebook's react example here, I found this code showing how to use mixins to set intervals. I am confused as to what is happening with this.intervals. I understand that state holds render-altering data, and props handle data handed down from a parent component, ideally. I would have used this.props.intervals instead, but what is the difference between the two?
var SetIntervalMixin = {
componentWillMount: function() {
this.intervals = [];
},
setInterval: function() {
this.intervals.push(setInterval.apply(null, arguments));
},
componentWillUnmount: function() {
this.intervals.forEach(clearInterval);
}
};
var TickTock = React.createClass({
mixins: [SetIntervalMixin], // Use the mixin
getInitialState: function() {
return {seconds: 0};
},
componentDidMount: function() {
this.setInterval(this.tick, 1000); // Call a method on the mixin
},
tick: function() {
this.setState({seconds: this.state.seconds + 1});
},
render: function() {
return (
<p>
React has been running for {this.state.seconds} seconds.
</p>
);
}
});
ReactDOM.render(
<TickTock />,
document.getElementById('example')
);
When you use props, you know for 100% certainty the value should will be coming from it's immediate parent component (as a property).
When you see state, you know the value is being born/created within that component it's self.
The key, when state changes, every child below will render if any of their received props change.
Your Mixin is not a normal React class. It is simply an object, so this in the case of this.interval, is a reference to the scope of the object in which the method is being executed - TickTock.
-I have a Component (let's call it StorageComponent) that get a data(JSON) from a restful api (the request is made in componentDidMount)
-StorageComponent then passes the data to a child component and it will do stuff with other components to display the data and interact with user.
-Now there is another hierarchy of components independent of the above.
-This handle some form inputs by users, there is one component for each form input (radio button, checkbox, text, etc). And because every re-render will get rid of any state of the child, I had to use an object literal (call it ModelObject) to store each form input. So whenever a user enter something in a form it will make a call to Modelobject and store it there, the component will also ask for data from ModelObject.
-After The user entered all input he will eventually hit a submit button component in this hierarchy, where it will make a call to ModelObject to do ajax POST to the RestAPI. My problem is here, I would like for ModelComponent to get the data from the RestAPI again, so user will see the updated data. I thought forceUpdate() would work, I thought it would re-trigger rendering and thus componentDidMount in StorageComponent.
So what is the best way to do this. Moreover is there any bad practice mentioned above? Is this enough information?
edit:
the storageComponent hierarchy
var StorageComponent= React.createClass({
getInitialState: function(){
return {
data: []
};
},
componentDidMount: function(){
this.serverRequest = $.get(this.props.source, function(result){
result = JSON.parse(result);
this.setState({
data: result
});
}.bind(this));
},
render: function() {
return (
<div>
<Nav dataList={this.state.data} /> //whole bunch of other child component below this one
</div>
);
}
});
app.storageComponent= React.render(
<HabitModel source = "/api/listing/user"/>,
document.getElementById('myDiv')
);
the ModelObject that I've mentioned:
var formModel = {
newInfo: {
inputBox: "",
frequency: "",
date: "",
days: []
},
addDescription: function(description){
this.newHabitInfo.description = description;
},
addFrequency: function(selection){
this.newHabitInfo.frequency = selection;
},
addDay: function(startDay){
this.newHabitInfo.startDay = startDay;
},
getFrequency: function(){
return this.newHabitInfo.frequency;
},
//this is the function I want the second hierarchy of components to
//use to force the storageComponent to do the re-trigger the ajax
updateHabitListing: function(){
if(this.validate()){
app.habitListing.forceUpdate();
}else{
console.log("form not finish");
}
}
All:
If I define a component have a property called "value",
var Child = React.createClass({
componentWillReceiveProps: function(){
console.log("componentWillReceiveProps",this.props.value);
},
shouldComponentUpdate : function(){
console.log("shouldComponentUpdate", this.props.value);
return true;
},
componentWillUpdate : function(){
console.log("componentWillUpdate", this.props.value);
},
componentDidUpdate: function(){
console.log("componentDidUpdate", this.props.value);
},
render: function(){
return (
<div>The value generated by Parent: {this.props.value}</div>
);
}
});
If I want to give the newly set props.value to state.value( or maybe prepare a value for transition/interpolation ), but all stages before render only have previous value. Could anyone show me how to get new value before render?
Thanks
Important Note: componentWillReceiveProps is deprecated: https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops
componentWillReceiveProps is called when a component receives new props.
From here you can update the component's state using setState without triggering a render.
You can access the new props from the first argument passed to
componentWillReceiveProps
You can access the old props this.props
From your example:
componentWillReceiveProps: function(nextProps){
console.log("componentWillReceiveProps", nextProps.value, this.props.value);
},
JSBin demo
For anybody finding this old question via Google, it's out of date. You shouldn't be using this function anymore and, moreover, there are other solutions that don't involve updating the state! Take a look at this react.js blog article, You Probably Don't Need Derived State.
It's not totally clear what OP wanted to do but there are various appropriate solutions in that article. In my case, I wanted to reset a popup window, when a different element was clicked. You can do this with the key attribute. It works like magic. :)
Can you tell me why when I do this:
var SomeComponent = React.createClass({
getData: function(){
if (this.isMounted()){
var queryInfo = {
userId: sessionStorage.getItem("user_id"),
userRole: sessionStorage.getItem('user_role'),
aptId : this.props.params
}
io = io.connect();
io.emit('allTasks', queryInfo);
io.on('allTasksInfo', function(data){
reqwest({
url: '/apartment/tasks/address',
method: 'get',
xhrFields: {withCredentials: true},
crossOrigin: true
}).then(function(data){
this.setState({
dataSet: arr
})
}.bind(this));
}.bind(this));
}
},
componentDidMount: function(){
this.getData();
},
render: function(){...}
});
The code inside the if is executed, but I get the Uncaught Error: Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op.
But when I do this:
var SomeComponent = React.createClass({
getData: function(){
var queryInfo = {
userId: sessionStorage.getItem("user_id"),
userRole: sessionStorage.getItem('user_role'),
aptId : location.pathname.split("/")[4]
}
reqwest({
url:'/operation/staff',
method: 'get',
xhrFields: {withCredentials: true},
crossOrigin: true
}).then(function(data){
if(this.isMounted()){
this.setState({
operationStaff: data
})
}
}.bind(this));
}
componentDidMount: function(){
this.getData();
},
render: function(){...}
});
Everything is ok. Shouldn't the first just be executed when the component is mounted? What I am missing?
EDIT: I'm using react-router and express server with socket.io with server rendering (just the components, not the data - this I will fetch client side). After the answers, I can say:
The component is not unmounting
I can now tell that at first render, this warning doesn't appear even on second example:
https://drive.google.com/file/d/0B1rbX9C6kejlbWVKeTZ6WVdGN0E/view?usp=sharing
But if I change the url and get back to this path (and here yes, the component unmounts off course), the Ajax reqwest is being called 2 times
https://drive.google.com/file/d/0B1rbX9C6kejlUjFRYTBtejVLZGs/view?usp=sharing
This has something to do with the sockets implementation.
I will close this issue and open another regarding this. Thank you for the help.
Shouldn't the first just be executed when the component is mounted?
Yes, and it is (what makes you think it is not?).
However, the Ajax callback itself is executed some time in the future and at that moment, the component may already be unmounted.
In the first example, the test is useless since the component is always mounted after componentDidMount was called. In the second example, you are testing whether the component is mounted just before you call setState, which makes more sense.
Here is a simplified example:
var Hello = React.createClass({
getInitialState: function() {
return {name: 'foo'};
},
componentDidMount: function() {
console.log('mounted...');
setTimeout(function() {
// this works fine
console.log('updating state once...');
this.setState({
name: 'bar'
});
}.bind(this), 1000);
setTimeout(function() {
// this will throw
console.log('updating state twice...');
this.setState({
name: 'baz'
});
}.bind(this), 3000);
},
componentWillUnmount: function() {
console.log('unmounting...');
},
render: function() {
return <div>Hello {this.state.name}</div>;
}
});
React.render(
<Hello />,
document.getElementById('container')
);
setTimeout(function() {
React.unmountComponentAtNode(
document.getElementById('container')
);
}, 2000);
If you run it you will notice that the second timeout will generate the same error because it is called after the component was unmounted:
Console output:
mounted...
updating state once...
unmounting...
updating state twice...
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op.
DEMO: https://jsfiddle.net/pkzfbcr5/
I'm using react-router and express server with socket.io with server rendering (just the components, not the data - this I will fetch client side). After the answers, I can say:
The component is not unmounting
I can now tell that at first render, this warning doesn't appear even on second example: https://drive.google.com/file/d/0B1rbX9C6kejlbWVKeTZ6WVdGN0E/view?usp=sharing
But if I change the url and get back to this path (and here yes, the component unmounts off course), the Ajax reqwest is being called 2 times https://drive.google.com/file/d/0B1rbX9C6kejlUjFRYTBtejVLZGs/view?usp=sharing
This has something to do with the sockets implementation.
I will close this issue and open another regarding this. Thank you for the help.