What are the differences when re-rendering after state was set with Hooks compared to the class-based approach?
Asked Answered
A

2

38

Class Components

In React class components, we are told that setState always causes a re-render, regardless of whether or not the state actually changed to a new value. In effect, a component will re-render, when state updates to the same value it was before.

Docs (setState API Reference):

setState() will always lead to a re-render unless shouldComponentUpdate() returns false.


Hooks (Function Components)

With hooks however, the docs specify that updating state to a value identical to the previous state, will not cause a re-render (of child components):

Docs (useState API Reference):

Bailing out of a state update

If you update a State Hook to the same value as the current state, React will bail out without rendering the children or firing effects. (React uses the Object.is comparison algorithm.)


Closely Related Questions

  1. Is it correct that this.setState in class components always cause a re-render, even when the new state value is identical to the previous?
  2. Is it correct that in function components with hooks, setState from useState only causes a re-render if the state value is different from the previous value?
  3. Is setting state with this.setState inside the render method of a class component, the same as setting state inside the function body of a function component with hooks?
  4. Is the following correct?
    • In a class component, if we set state in the render method an infinite loop will occur. This is because the class component does not care that the new state is the same as the previous state. It just keeps re-rendering on every this.setState.
    • In a function component with hooks however, setting state inside the function body (which runs at re-render similarly to the render method in class components) would not be an issue, because the function component just bails out of re-renders when it sees that the state is identical to the previous state.
Astrid answered 27/3, 2019 at 9:34 Comment(3)
we are told that setState always causes a re-render i'm not sure about this. If you return null this.setState(() => null), it does not re-render anything.Oleate
What exactly do you mean by 3? setState call?Abdication
@estus Yes, clarified the OP.Astrid
A
34

Is it correct that this.setState in class components always cause a re-render, even when the new state value is identical to the previous?

If you set a valid value apart from returning null within setState, a re-render will always be triggered by react in a class component unless your component is a PureComponent or you implement shouldComponentUpdate

Is it correct that in function components with hooks, setState from useState only causes a re-render if the state value is different from the previous value?

For a functional component using useState hook, the setter if called with the same state will not trigger a re-render. However for an occasional case if the setter is called immediately it does result in two renders instead of one

Is setting state with this.setState inside the render method of a class component, the same as setting state inside the function body of a function component with hooks?

Techincally yes, setting a state directly in render method will cause the function to trigger re-render in case of class component causing an infinite loop which is the case for functional components provided the state values are different. Regardless of that, it will still cause an issue because any other state update will be reverted back due to functional component calling state update directly

In a class component, if we set state in the render method an infinite loop will occur. This is because the class component does not care that the new state is the same as the previous state. It just keeps re-rendering on every this.setState.

Yes, hence its recommended not to call setState directly in render

In a function component with hooks however, setting state inside the function body (which runs at re-render similarly to the render method in class components) would not be an issue, because the function component just bails out of re-renders when it sees that the state is identical to the previous state.

Not 100% true, since you can trigger state update using previous value such that the previous and current value are not same.For example

setCount(count => count + 1);

In such a case, you component will still fall in an infinite loop

Alert answered 27/3, 2019 at 9:46 Comment(6)
If you implement shouldComponentUpdate to return false if the state or props hasn't changed, then you component won't re-renderAlert
@Magnus, yeah typo on my part. Updated the ansAlert
Thank you, deeply appreciated. Where do you find all this info? The official docs have very little info on when and why re-renders happen. They should really create a page on that topic...Astrid
I have tested the above scenarios one way or another over timeAlert
Just to be 100% certain, the function body of a functional component is similar to the contents of the render method in a class component, right? I mean, on re-render of a functional component, the function body executes, and on re-render of a class component, the render method executes. Is that correct?Astrid
@Magnus, yes you are right. the effects are an additional logic to a functional component which isn't present in render for class componentAlert
G
1

This is not a direct answer to the OP, but related and maybe helpful for some people new to React and/or Hooks and struggling with their side-effect and render timing.

Since it hasn't been mentioned here yet: In functional components rather than using the beforementioned (see comments of the accepted answer) ShouldComponentUpdate() function, which is for class-based Components only, you would use the useEffect() hook. With it you can tell your component when to run the side effects AND under which condition, e.g. when certain dependencies have changed.

In this example from the React docs, only when props.source changed, the function will be executed.

useEffect(
  () => {
    const subscription = props.source.subscribe();
    return () => {
      subscription.unsubscribe();
    };
  },
  [props.source],
);

React docs: useEffect()

Gunboat answered 26/11, 2019 at 12:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.