Logo

Does render get called any time "setState" is called in reactjs?

By default, yes, React will call the render method (in a class component) or re-run the component function (in a function component) every time you call setState. When you invoke setState, React schedules a re-render for that component and potentially its descendants. However, React applies optimizations and may batch multiple state updates together. Below, we’ll explore some nuances around when and how render gets called in both class and function components.

1. Class Components

The Default Behavior

In a class component, any call to this.setState() marks the component for re-render. React queues up these state changes, and on the next render cycle, React will call render() for that component.

class MyComponent extends React.Component { state = { count: 0 }; increment = () => { this.setState((prevState) => ({ count: prevState.count + 1 })); }; render() { console.log('Render called!'); return ( <button onClick={this.increment}> Count: {this.state.count} </button> ); } }

Clicking the button repeatedly will call setState, which in turn causes render() to be called each time—though React may batch these updates to reduce re-renders if they happen very quickly.

Conditional Rendering via shouldComponentUpdate

If you implement the shouldComponentUpdate(nextProps, nextState) lifecycle method (or PureComponent, or memoization techniques), you can override the default behavior. This allows you to return false and prevent an update cycle—thereby skipping the render() call—even if setState was invoked.

shouldComponentUpdate(nextProps, nextState) { // Return true or false based on comparison of current vs. next props/state return nextState.count !== this.state.count; }

2. Function Components with Hooks

Re-render on State Updates

In function components, using useState or other hooks also triggers a re-render whenever state changes:

function MyFunctionalComponent() { const [count, setCount] = React.useState(0); const increment = () => setCount((prev) => prev + 1); console.log('Render called!'); return ( <button onClick={increment}> Count: {count} </button> ); }

Each time you call setCount, React marks your component for re-render, and the entire function runs again (including its return JSX).

Memoization with React.memo

To skip unnecessary renders in function components, you can wrap them in React.memo. This higher-order component does a shallow comparison of props. If props don’t change, React can skip re-rendering:

const MyMemoizedComponent = React.memo(function MyMemo({ value }) { console.log('Render called!'); return <div>{value}</div>; });

However, internal state updates within a memoized component will still cause re-renders—React.memo only memoizes with respect to props. If you want to skip re-renders based on state, you’ll need other optimizations.

3. Batching of State Updates

In modern React, multiple setState or useState calls within the same event loop may get batched, resulting in fewer actual re-renders. This means React can combine multiple state changes before calling render(), improving performance. However, each batch still leads to at least one re-render if the state changes.

4. When Render Might Not Be Called

  • shouldComponentUpdate returning false: In class components, if shouldComponentUpdate() returns false, the re-render is skipped.
  • React.PureComponent: Internally uses shallow props and state comparison. If nothing changes, render() is not called.
  • React.memo: For function components, if props are unchanged (and there’s no internal state change), React can skip re-rendering.

Best Practices and Tips

  1. Avoid Unnecessary Re-renders
    • Use memoization (React.memo, useMemo, or useCallback) or pure components to prevent expensive re-renders.
  2. Don’t Over-optimize Too Early
    • Overusing memoization can lead to complexity. Optimize only where performance issues are real.
  3. Rely on React’s Batching
    • State updates within the same event loop can be batched, minimizing performance overhead.

Conclusion

In most cases, calling setState (or a state setter in function components) triggers a re-render. This is the default React behavior, ensuring your UI stays in sync with your state. You can override this behavior using lifecycle methods like shouldComponentUpdate, PureComponent, or wrapping function components in React.memo if you want to skip updates. Regardless, React’s reconciliation process, combined with batching, helps ensure that your components render efficiently under typical use cases.

CONTRIBUTOR
TechGrind