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();
}
Related
-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");
}
}
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" :)
I am pretty new to FLUX, so I started with the TODO example on their website:
https://facebook.github.io/flux/docs/todo-list.html
In the Listening to Changes with a Controller-View part:
There is on event binding in TodoApp:
componentDidMount: function() {
TodoStore.addChangeListener(this._onChange);
}
and this._onChange is like:
_onChange: function() {
this.setState(getTodoState());
}
The addChangeListener from TodoStore is like:
addChangeListener: function(callback) {
this.on(CHANGE_EVENT, callback);
}
What confused me here is:
It seems that the store just simply register that _onChange(), but how does the TodoStore know whose _onChange() need to be called if there are multiple TodoApps rendered on the page.
TodoStore simply emits the event and every component that is listening to that store will update. So if you have two TodoApps on page, both will update.
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. :)
This may be treading that line between answerable and opinionated, but I'm going back and forth as to how to structure a ReactJS component as complexity grows and could use some direction.
Coming from AngularJS, I want to pass my model into the component as a property and have the component modify the model directly. Or should I be splitting the model up into various state properties and compiling it back together when sending back upstream? What is the ReactJS way?
Take the example of a blog post editor. Trying to modify the model directly ends up looking like:
var PostEditor = React.createClass({
updateText: function(e) {
var text = e.target.value;
this.props.post.text = text;
this.forceUpdate();
},
render: function() {
return (
<input value={this.props.post.text} onChange={this.updateText}/>
<button onClick={this.props.post.save}/>Save</button>
);
}
});
Which seems wrong.
Is it more the React way to make our text model property state, and compile it back into the model before saving like:
var PostEditor = React.createClass({
getInitialState: function() {
return {
text: ""
};
},
componentWillMount: function() {
this.setState({
text: this.props.post.text
});
},
updateText: function(e) {
this.setState({
text: e.target.value
});
},
savePost: function() {
this.props.post.text = this.state.text;
this.props.post.save();
},
render: function() {
return (
<input value={this.state.text} onChange={this.updateText}/>
<button onClick={this.savePost}/>Save</button>
);
}
});
This doesn't require a call to this.forceUpdate(), but as the model grows, (a post may have an author, subject, tags, comments, ratings, etc...) the component starts getting really complicated.
Is the first method with ReactLink the way to go?
Updating 2016:
React is changed, and explanation "props vs state" became very simple. If a component needs to change data - put it in a state, otherwise in props. Because props are read-only now.
What's the exact difference between props and state?
You can find good explanation here (full version)
Your second approach is more like it. React doesn't care about models so much as it cares about values and how they flow through your app. Ideally, your post model would be stored in a single component at the root. You then create child components that each consume parts of the model.
You can pass callbacks down to the children that need to modify your data, and call them from the child component.
Modifying this.props or this.state directly is not a good idea, because React will not be able to pick up on the changes. That's because React does a shallow comparison of your post prop to determine if it has changed.
I made this jsfiddle to show how data could flow from an outer to an inner component.
The handleClick method shows 3 ways to (im)properly update state:
var Outer = React.createClass({
getInitialState: function() {
return {data: {value: 'at first, it works'}};
},
handleClick: function () {
// 1. This doesn't work, render is not triggered.
// Never set state directly because the updated values
// can still be read, which can lead to unexpected behavior.
this.state.data.value = 'but React will never know!';
// 2. This works, because we use setState
var newData = {value: 'it works 2'};
this.setState({data: newData});
// 3. Alternatively you can use React's immutability helpers
// to update more complex models.
// Read more: http://facebook.github.io/react/docs/update.html
var newState = React.addons.update(this.state, {
data: {value: {$set: 'it works'}}
});
this.setState(newState);
},
render: function() {
return <Inner data={this.state.data} handleClick={this.handleClick} />;
}
});
From React doc
props are immutable: they are passed from the parent and are "owned" by the parent. To implement interactions, we introduce mutable state to the component. this.state is private to the component and can be changed by calling this.setState(). When the state is updated, the component re-renders itself.
From TrySpace: when props (or state) are updated (via setProps/setState or parent) the component re-renders as well.
A reading from Thinking in React:
Let's go through each one and figure out which one is state. Simply
ask three questions about each piece of data:
Is it passed in from a parent via props? If so, it probably isn't
state.
Does it change over time? If not, it probably isn't state.
Can you compute it based on any other state or props in your
component? If so, it's not state.
I'm not sure if I'm answering your question, but I've found that, especially in a large/growing application, the Container/Component pattern works incredibly well.
Essentially you have two React components:
a "pure" display component, which deals with styling and DOM interaction;
a container component, which deals with accessing/saving external data, managing state, and rendering the display component.
Example
N.B. This example is a probably too simple to illustrate the benefits of this pattern, as it is quite verbose for such a straightforward case.
/**
* Container Component
*
* - Manages component state
* - Does plumbing of data fetching/saving
*/
var PostEditorContainer = React.createClass({
getInitialState: function() {
return {
text: ""
};
},
componentWillMount: function() {
this.setState({
text: getPostText()
});
},
updateText: function(text) {
this.setState({
text: text
});
},
savePost: function() {
savePostText(this.state.text);
},
render: function() {
return (
<PostEditor
text={this.state.text}
onChange={this.updateText.bind(this)}
onSave={this.savePost.bind(this)}
/>
);
}
});
/**
* Pure Display Component
*
* - Calculates styling based on passed properties
* - Often just a render method
* - Uses methods passed in from container to announce changes
*/
var PostEditor = React.createClass({
render: function() {
return (
<div>
<input type="text" value={this.props.text} onChange={this.props.onChange} />
<button type="button" onClick={this.props.onSave} />
</div>
);
}
});
Benefits
By keeping display logic and data/state management separate, you have a re-usable display component which:
can easily be iterated with different sets of props using something like react-component-playground
can be wrapped with a different container for different behavior (or combine with other components to build larger parts of your application
You also have a container component which deals with all external communication. This should make it easier to be flexible about the way you access your data if you make any serious changes later on*.
This pattern also makes writing and implementing unit tests a lot more straightforward.
Having iterated a large React app a few times, I've found that this pattern keeps things relatively painless, especially when you have larger components with calculated styles or complicated DOM interactions.
*Read up on the flux pattern, and take a look at Marty.js, which largely inspired this answer (and I have been using a lot lately) Redux (and react-redux), which implement this pattern extremely well.
Note for those reading this in 2018 or later:
React has evolved quite a bit since this answer was written, especially with the introduction of Hooks. However, the underlying state management logic from this example remains the same, and more importantly, the benefits that you get from keeping your state and presentation logic separate still apply in the same ways.
I think you're using an anti-pattern which Facebook has already explained at this link
Here's thing you're finding:
React.createClass({
getInitialState: function() {
return { value: { foo: 'bar' } };
},
onClick: function() {
var value = this.state.value;
value.foo += 'bar'; // ANTI-PATTERN!
this.setState({ value: value });
},
render: function() {
return (
<div>
<InnerComponent value={this.state.value} />
<a onClick={this.onClick}>Click me</a>
</div>
);
}
});
The first time the inner component gets rendered, it will have { foo: 'bar' } as the value prop. If the user clicks on the anchor, the parent component's state will get updated to { value: { foo: 'barbar' } }, triggering the re-rendering process of the inner component, which will receive { foo: 'barbar' } as the new value for the prop.
The problem is that since the parent and inner components share a reference to the same object, when the object gets mutated on line 2 of the onClick function, the prop the inner component had will change. So, when the re-rendering process starts, and shouldComponentUpdate gets invoked, this.props.value.foo will be equal to nextProps.value.foo, because in fact, this.props.value references the same object as nextProps.value.
Consequently, since we'll miss the change on the prop and short circuit the re-rendering process, the UI won't get updated from 'bar' to 'barbar'