What does the setState() function run? Does it only run render()?
What does the setState() function run? Does it only run render()
No setState not only calls the render() function but after setState, the following lifecycle functions will run in order depending on what shouldComponentUpdate returns
if shouldComponentUpdate returns true(which is true by default).
1. shouldComponentUpdate
2. componentWillUpdate
3. render()
4. componentDidUpdate
if shouldComponentUpdate returns false(if you have a custom implementation)
1. shouldComponentUpdate
One more thing to know about setState is that, it only triggers the re-render for the current component and all its children(considering no implementation of shouldComponentUpdate for any of its children), Its doesn't trigger a re-render of the parent component and hence the reconcilation doesn't happen for the parent components but only for itself and its children.
A DEMO of what happens when setState is called.
class App extends React.Component {
state = {
count: 0
}
componentWillReceiveProps(nextProps) {
console.log('componentWillReceiveProps parent');
}
shouldComponentUpdate() {
console.log('shouldComponentUpdate parent');
return true;
}
componentWillUpdate() {
console.log('componentWillUpdate parent');
}
render() {
console.log('render parent')
return (
<div>
<Child count = {this.state.count}/>
<button onClick={() => {
console.log('callingsetState');this.setState((prevState) => ({count: prevState.count + 1}))}} >Increase</button>
</div>
)
}
componentDidUpdate() {
console.log('componentDidUpdate parent')
}
}
class Child extends React.Component {
componentWillMount() {
console.log('componentWillMount child');
}
componentDidMount() {
console.log('componentDidMount child');
}
componentWillReceiveProps(nextProps) {
console.log('componentWillReceiveProps child');
}
shouldComponentUpdate() {
console.log('shouldComponentUpdate child');
return true;
}
componentWillUpdate() {
console.log('componentWillUpdate child');
}
render() {
console.log('child')
return (
<div>
<div>{this.props.count}</div>
</div>
)
}
componentDidUpdate() {
console.log('componentDidUpdate child')
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
To add an explanation for the question that #poepje added on your question
What setState does?
setState() enqueues changes to the component state and tells React that this component and its children need to be re-rendered with the
updated state. This is the primary method you use to update the user
interface in response to event handlers and server responses.
React has a very good documentation on this function here
You could also see the following answer on how setState works:
setState doesn't update the state immediately
The setState() will run functions in this order:
shouldComponentUpdate()
componentWillUpdate()
render()
componentDidUpdate()
If your component is receiving props it will run the componentWillRecieveProps() function with the above functions.
The first thing React will do when setState is called is merged the object you passed into setState into the current state of the component. This will kick off a process called reconciliation. The end goal of reconciliation is to, in the most efficient way possible, update the UI based on this new state.
To do this, React will construct a new tree of React elements (which you can think of as an object representation of your UI). Once it has this tree, in order to figure out how the UI should change in response to the new state, React will diff this new tree against the previous element tree.
By doing this, React will then know the exact changes which occurred, and by knowing exactly what changes occurred, will able to minimize its footprint on the UI by only making updates where absolutely necessary.
The setState() will run functions in this order:
shouldComponentUpdate()
componentWillUpdate()
render()
componentDidUpdate()
If your component is receiving props it will run the componentWillRecieveProps() function with the above functions.
Just an update to this answer:
https://stackoverflow.com/a/45273993/7310034
(since lifeCycle methods are now updated)
setState() will run functions in this order:
getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapBeforeUpdate (if exists)
componentDidUpdate()
According to this:
http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
Related
have a question, how to check if react component detached from its parent components?
Let’s say I have a react component, that is subscribed to any async events: WebSocket, timer.
I just want to not waste computer resources on listener to this event, when react component no more in use, and deallocate react component as well.
Any though?
React provides various methods to keep track of a component's lifecycle. And in your case you need to track if a component has unmounted. So, there are 2 approach for this based on the type of component you are using:
Class Component
Use componentUnmount lifecycle method.
class YourComponent extends Component {
constructor(props) {
super(props);
}
componentWillUnmount() {
// this method is invoked immediately before a component
// is unmounted and destroyed. you can perform any necessary
// cleanup in this method, such as invalidating
// timers, canceling network requests,
// or cleaning up subscriptions
}
render() {
return (
<div>
{/* ...contents... */}
</div>
);
}
}
Functional Component
Leverage useEffect hook with cleanup.
const YourComponent = () => {
useEffect(() => {
// rest of code
return () => {
// similar to componentWillUnmount() method, this function
// would invoke at the time of component's unmount.
};
},[]);
return (
<div>
{/* ...contents... */}
</div>
);
}
I'm currently playing around with ReactJS' PureComponent. I have a simple component which just shows some text inside nested PureComponents:
export class Test extends React.Component<ITestProps> {
componentDidMount(): void {
window.setInterval(() => this.forceUpdate(), 1500);
}
private readonly extraSmall = { size: 10 };
render(): JSX.Element {
console.log("render Login");
return (
<Bootstrap.Container fluid={true}>
<Bootstrap.Row>
<Bootstrap.Col xs={this.extraSmall}>
RENDERED!
</Bootstrap.Col>
</Bootstrap.Row>
</Bootstrap.Container>
);
}
}
I've exptected that the render would only be called once on each component. Container, Row and Col are all PureComponents.
However, they all got called once every 1.5 seconds and I don't get the point why.
What I have understood from the docs is, that even if the parent is updated during forceUpdate(), each child will call the shouldComponentUpdate which should return false for each child of Test or at least Container.
But in console is see render Login, render Container, render Row and render Col. But Container's props or state did not change. So why is there a re-render happening?
From the docs:
Calling forceUpdate() will cause render() to be called on the component, skipping shouldComponentUpdate().
This will trigger the normal lifecycle methods for child components, including the shouldComponentUpdate()
method of each child. React will still only update the DOM if the markup changes.
So even if this component does not make any real-life sense, it should not re-render at least Row and Col.
Maybe I am too late, but I will leave this answer for other users.
The reason is you are using forceUpdate which ignores shouldComponentUpdate.
The reason of forceUpdate existence is to make component re-render again and again ignoring props or state update, because sometimes it is needed.
Docs reference:
https://reactjs.org/docs/react-component.html#forceupdate
This is how we use componentWillReceiveProps
componentWillReceiveProps(nextProps) {
if(nextProps.myProp !== this.props.myProps) {
// nextProps.myProp has a different value than our current prop
}
}
It's very similar to componentDidUpdate
componentDidUpdate(prevProps) {
if(prevProps.myProps !== this.props.myProp) {
// this.props.myProp has a different value
// ...
}
}
I can see some differences, like if I do setState in componentDidUpdate, render will trigger twice, and the argument for componentWillReceiveProps is nextProps, while argument for componentDidUpdate is prevProp, but seriously I don't know when to use them. I often use componentDidUpdate, but with prevState, like change a dropdown state and call api
eg.
componentDidUpdate(prevProps, prevState) {
if(prevState.seleted !== this.state.seleted) {
this.setState({ selected: something}, ()=> callAPI())
}
}
The main difference between the two is:
When they are called in a component's lifecycle
How it updates component state
When are they called?
As the names suggest – and as you probably know since you mentioned "if I do setState in componentDidUpdate, render will trigger twice" – componentDidUpdate is called after the component updates (received new props or state). This is why the parameters to this function is prevProps and prevState.
So if you wanted to do something before the component received new props, you'd use componentWillReceiveProps, and if you wanted to do something after it received new props or state, you'd use componentDidUpdate.
How do they update state?
The main difference here is:
componentWillReceiveProps will update state synchronously
componentDidUpdate will update state asynchronously.
This can be important as there are some gotchya's that can come up when trying to sync state with other parts of your component's props.
I have two components, parent component App and child component SearchBar, I want SearchBar to keep its own state, and after updating its state to call a function given by its parent as a prop that will update its parent state.
So on my SearchBar component I have
onSearchChange(event) {
.
.
.
this.setState({ searchTerm });
}
And then
componentDidUpdate(){
this.props.onSearchChange(this.state.searchTerm)
}
And on the parent component
onSearchChange(searchTerm){
this.setState({searchTerm});
}
render() {
return (
<div className="App">
<div>
<SearchBar onSearchChange={this.onSearchChange}/>
</div>
.
.
.
</div>
);
}
But this causes an infinite loop where SearchBar's componentDidUpdate gets called which calls its parent onSearchChange to update the parent state but then SearchBar's componentDidUpdate its called again an so on.
If I use a callback in setState instead of componentDidUpdate to update its parent state it works correctly, but I just don't understand why SearchBar its updating if its prop its constant.
componentDidUpdate provides two arguments prevProps and prevState, with it you can check if actual props or state has changed and then make changes to current state/props.. for example:
componentDidUpdate(prevProps, prevState){
if(prevState.searchTerm !== this.state.searchTerm) {
this.props.onSearchChange(this.state.searchTerm)
}
}
Edit: Check out the git repository for a minmal example: https://github.com/maximilianschmitt/blind-lifecycle
I have a component RequireUser that tries to ensure that the user is logged in and will otherwise not render its children. Its parent component, App, should know if a user is required and render a login form if needed.
The problem is, that the App component mounts AFTER the RequireUser component in a tree like this:
App
RequireUser
SomeOtherComponent
In RequireUser's componentDidMount I am triggering an action requireLogin that sets the UserStore's loginRequired variable to true.
This does not update the parent component (App) because it has not yet been mounted and can therefor not register changes to the store.
class RequireUser extends React.Component {
constructor() {
super();
this.state = alt.stores.UserStore.getState();
}
componentDidMount() {
this.unlisten = alt.stores.UserStore.listen(this.setState.bind(this));
if (!this.state.requireUser) {
UserActions.requireUser();
// using setTimeout will work:
// setTimeout(() => UserActions.requireUser());
}
}
componentWillUnmount() {
this.unlisten();
}
render() {
if (this.state.requireUser) {
return <div>I have required your user</div>;
}
return <div>I will require your user</div>;
}
}
class App extends React.Component {
constructor() {
super();
this.state = alt.stores.UserStore.getState();
}
componentDidMount() {
this.unlisten = alt.stores.UserStore.listen(this.setState.bind(this));
}
componentWillUnmount() {
this.unlisten();
}
render() {
return (
<div>
<div>User required? {this.state.requireUser + ''}</div>
<RequireUser />
</div>
);
}
}
Output:
User required? false
I have required your user
If I use setTimeout in RequireUser, App receives the state changes and renders, but only after a flicker:
User required? true
I have required your user
I have the feeling what I am doing is an anti-pattern and I would be grateful for suggestions of a more elegant solution than flickering with setTimeout. Thanks!
My suggested answer is to add this to the App component:
componentDidMount() {
// setup listener for subsequent changes
alt.stores.UserStore.listen(this.onChange);
// grab the current state now that we're mounted
var userStoreState = alt.stores.UserStore.getState();
this.setState(userStoreState);
}
There is no way to avoid the double render. Your RequireUser component already performs two renders.
Initial render of RequireUser
componentDidMount() callback
an action is dispatched
UserStore receives the dispatched action and updates its state
change notification is emitted
RequireUser sets state based on the state change
Second render of RequireUser
But your codebase is still considered Flux, and indeed follows the pattern intended for React apps. Essentially, you have a loading state... a state where we don't actually know if we need to require a user or not. Depending on what UserActions.requireUser() does, this may or may not be desired.
You might consider a refactor
You can fix the double-render if you rewrite RequireUser as a view-only component. This means no listeners nor setting state internally. This component simply renders elements based on the props passed in. This is literally all your RequireUser component would be:
class RequireUser extends React.Component {
render() {
if (this.props.requireUser) {
return <div>I have required your user</div>;
}
return <div>I will require your user</div>;
}
}
You will then make your App component a controller-view. The listener is added here, and any changes to state are propagated downward by props. Now we can setup in the componentWillMount callback. This gives us the single render behavior.
class App extends React.Component {
(other lifecycle methods)
componentWillMount() {
if (!this.state.requireUser) {
UserActions.requireUser();
}
var userStoreState = alt.stores.UserStore.getState();
this.setState(userStoreState);
}
componentDidMount() {
(same as above)
}
render() {
return (
<div>
<div>User required? {this.state.requireUser + ''}</div>
<RequireUser requireUser={this.state.requireUser} />
</div>
);
}
}
Flux architecture and controller-views/views: https://facebook.github.io/flux/docs/overview.html#views-and-controller-views
Your components each only gets the states from your Store once - only during the construction of each components. This means that the states in your components will NOT be in sync with the states in the store
You need to set up a store listeners on your components upon mounting in order to retrieve a trigger from the store and the most up-to-date states. Use setState() to update the states inside the component so render() will be called again to render the up-to-date states
What about putting the store listener in the constructor? That worked for me.