<100 subscribers
Share Dialog
Share Dialog
Update: I gave a follow up talk on this topic at React Rally. While this post is more about the “functional setState” pattern, the talk is more about understanding setState deeply
React has popularized functional programming in JavaScript. This has led to giant frameworks adopting the Component-based UI pattern that React uses. And now functional fever is spilling over into the web development ecosystem at large.
But the React team is far from relenting. They continue to dig deeper, discovering even more functional gems hidden in the legendary library.
So today I reveal to you a new functional gold buried in React, best kept React secret — Functional setState!
Okay, I just made up that name… and it’s not entirely new or a secret. No, not exactly. See, it’s a pattern built into React, that’s only known by few developers who’ve really dug in deep. And it never had a name. But now it does — Functional setState!
Going by
’s words in describing this pattern,Functional setStateis a pattern where you
“Declare state changes separately from the component classes.”
Huh?
React is a component based UI library. A component is basically a function that accept some properties and return a UI element.
function User(props) {
return (
<div>A pretty user</div>
);
}
Update: I gave a follow up talk on this topic at React Rally. While this post is more about the “functional setState” pattern, the talk is more about understanding setState deeply
React has popularized functional programming in JavaScript. This has led to giant frameworks adopting the Component-based UI pattern that React uses. And now functional fever is spilling over into the web development ecosystem at large.
But the React team is far from relenting. They continue to dig deeper, discovering even more functional gems hidden in the legendary library.
So today I reveal to you a new functional gold buried in React, best kept React secret — Functional setState!
Okay, I just made up that name… and it’s not entirely new or a secret. No, not exactly. See, it’s a pattern built into React, that’s only known by few developers who’ve really dug in deep. And it never had a name. But now it does — Functional setState!
Going by
’s words in describing this pattern,Functional setStateis a pattern where you
“Declare state changes separately from the component classes.”
Huh?
React is a component based UI library. A component is basically a function that accept some properties and return a UI element.
function User(props) {
return (
<div>A pretty user</div>
);
}
A component might need to have and manage its state. In that case, you usually write the component as a class. Then you have its state live in the class constructor function:
class User {
constructor () {
this.state = {
score : 0
};
} render () {
return (
<div>This user scored {this.state.score}</div>
);
}
}
To manage the state, React provides a special method called setState(). You use it like this:
class User {
... increaseScore () {
this.setState({score : this.state.score + 1});
} ...
}
Note how setState() works. You pass it an object containing part(s) of the state you want to update. In other words, the object you pass would have keys corresponding to the keys in the component state, then setState() updates or sets the state by merging the object to the state. Thus, “set-State”.
Remember how we said setState() works? Well, what if I told you that instead of passing an object, you could pass a function?
Yes. setState() also accepts a function. The function accepts the previous state and current props of the component which it uses to calculate and return the next state. See it below:
this.setState(function (state, props) {
return {
score: state.score - 1
}
});
Note that setState() is a function, and we are passing another function to it(functional programming… functional setState) . At first glance, this might seem ugly, too many steps just to set-state. Why will you ever want to do this?
The thing is, state updates may be asynchronous.
Think about what happens when setState() is called. React will first merge the object you passed to setState() into the current state. Then it will start that reconciliation thing. It will create a new React Element tree (an object representation of your UI), diff the new tree against the old tree, figure out what has changed based on the object you passed to setState() , then finally update the DOM.
Whew! So much work! In fact, this is even an overly simplified summary. But trust in React!
React does not simply “set-state”.
Because of the amount of work involved, calling setState() might not immediately update your state.
React may batch multiple
setState()calls into a single update for performance.
What does React mean by this?
First, “multiple setState() calls” could mean calling setState() inside a single function more than once, like this:
...state = {score : 0};// multiple setState() calls
increaseScoreBy3 () {
this.setState({score : this.state.score + 1});
this.setState({score : this.state.score + 1});
this.setState({score : this.state.score + 1});
}...
Now when React, encounters “multiple setState() calls”, instead of doing that “set-state” three whole times, React will avoid that huge amount of work I described above and smartly say to itself: “No! I’m not going to climb this mountain three times, carrying and updating some slice of state on every single trip. No, I’d rather get a container, pack all these slices together, and do this update just once.” And that, my friends, is batching!
Remember that what you pass to setState() is a plain object. Now, assume anytime React encounters “multiple setState() calls”, it does the batching thing by extracting all the objects passed to each setState() call, merges them together to form a single object, then uses that single object to do setState() .
In JavaScript merging objects might look something like this:
const singleObject = Object.assign(
{},
objectFromSetState1,
objectFromSetState2,
objectFromSetState3
);
This pattern is known as object composition.
In JavaScript, the way “merging” or composing objects works is: if the three objects have the same keys, the value of the key of the last object passed to Object.assign() wins. For example:
const me = {name : "Justice"},
you = {name : "Your name"},
we = Object.assign({}, me, you);we.name === "Your name"; //trueconsole.log(we); // {name : "Your name"}
Because you are the last object merged into we, the value of name in the you object — “Your name” — overrides the value of name in the me object. So “Your name” makes it into the we object… you win! :)
Thus, if you call setState() with an object multiple times — passing an object each time — React will merge. Or in other words, it will compose a new object out of the multiple objects we passed it. And if any of the objects contains the same key, the value of the key of the last object with same key is stored. Right?
That means that, given our increaseScoreBy3 function above, the final result of the function will just be 1 instead of 3, because React did not immediately update the state in the order we called setState() . But first, React composed all the objects together, which results to this: {score : this.state.score + 1} , then only did “set-state” once — with the newly composed object. Something like this: User.setState({score : this.state.score + 1}.
To be super clear, passing object to setState() is not the problem here. The real problem is passing object to setState() when you want to calculate the next state from the previous state. So stop doing this. It’s not safe!
Because
this.propsandthis.statemay be updated asynchronously, you should not rely on their values for calculating the next state.
A component might need to have and manage its state. In that case, you usually write the component as a class. Then you have its state live in the class constructor function:
class User {
constructor () {
this.state = {
score : 0
};
} render () {
return (
<div>This user scored {this.state.score}</div>
);
}
}
To manage the state, React provides a special method called setState(). You use it like this:
class User {
... increaseScore () {
this.setState({score : this.state.score + 1});
} ...
}
Note how setState() works. You pass it an object containing part(s) of the state you want to update. In other words, the object you pass would have keys corresponding to the keys in the component state, then setState() updates or sets the state by merging the object to the state. Thus, “set-State”.
Remember how we said setState() works? Well, what if I told you that instead of passing an object, you could pass a function?
Yes. setState() also accepts a function. The function accepts the previous state and current props of the component which it uses to calculate and return the next state. See it below:
this.setState(function (state, props) {
return {
score: state.score - 1
}
});
Note that setState() is a function, and we are passing another function to it(functional programming… functional setState) . At first glance, this might seem ugly, too many steps just to set-state. Why will you ever want to do this?
The thing is, state updates may be asynchronous.
Think about what happens when setState() is called. React will first merge the object you passed to setState() into the current state. Then it will start that reconciliation thing. It will create a new React Element tree (an object representation of your UI), diff the new tree against the old tree, figure out what has changed based on the object you passed to setState() , then finally update the DOM.
Whew! So much work! In fact, this is even an overly simplified summary. But trust in React!
React does not simply “set-state”.
Because of the amount of work involved, calling setState() might not immediately update your state.
React may batch multiple
setState()calls into a single update for performance.
What does React mean by this?
First, “multiple setState() calls” could mean calling setState() inside a single function more than once, like this:
...state = {score : 0};// multiple setState() calls
increaseScoreBy3 () {
this.setState({score : this.state.score + 1});
this.setState({score : this.state.score + 1});
this.setState({score : this.state.score + 1});
}...
Now when React, encounters “multiple setState() calls”, instead of doing that “set-state” three whole times, React will avoid that huge amount of work I described above and smartly say to itself: “No! I’m not going to climb this mountain three times, carrying and updating some slice of state on every single trip. No, I’d rather get a container, pack all these slices together, and do this update just once.” And that, my friends, is batching!
Remember that what you pass to setState() is a plain object. Now, assume anytime React encounters “multiple setState() calls”, it does the batching thing by extracting all the objects passed to each setState() call, merges them together to form a single object, then uses that single object to do setState() .
In JavaScript merging objects might look something like this:
const singleObject = Object.assign(
{},
objectFromSetState1,
objectFromSetState2,
objectFromSetState3
);
This pattern is known as object composition.
In JavaScript, the way “merging” or composing objects works is: if the three objects have the same keys, the value of the key of the last object passed to Object.assign() wins. For example:
const me = {name : "Justice"},
you = {name : "Your name"},
we = Object.assign({}, me, you);we.name === "Your name"; //trueconsole.log(we); // {name : "Your name"}
Because you are the last object merged into we, the value of name in the you object — “Your name” — overrides the value of name in the me object. So “Your name” makes it into the we object… you win! :)
Thus, if you call setState() with an object multiple times — passing an object each time — React will merge. Or in other words, it will compose a new object out of the multiple objects we passed it. And if any of the objects contains the same key, the value of the key of the last object with same key is stored. Right?
That means that, given our increaseScoreBy3 function above, the final result of the function will just be 1 instead of 3, because React did not immediately update the state in the order we called setState() . But first, React composed all the objects together, which results to this: {score : this.state.score + 1} , then only did “set-state” once — with the newly composed object. Something like this: User.setState({score : this.state.score + 1}.
To be super clear, passing object to setState() is not the problem here. The real problem is passing object to setState() when you want to calculate the next state from the previous state. So stop doing this. It’s not safe!
Because
this.propsandthis.statemay be updated asynchronously, you should not rely on their values for calculating the next state.
No comments yet