Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

shouldComponentUpdate equivalent for functional component, to ignore state changes

My code has a component that takes both props and has its own internal state.
The component should rerender ONLY when its props change. State changes should NOT trigger a rerender.
This behaviour can be implemented with a class based component and a custom shouldComponentUpdate function.
However, this would be the first class based component in the codebase. Everything is done with functional components and hooks. Therefore I would like to know whether it is possible to code the desired functionality with functional components.

After a few answers that didn't approach the real problem, I think I have to reformulate my question. Here is a minimal example with two components:

  • Inner takes a prop and has state. This is the component in question. It must not rerender after state changes. Prop changes should trigger a rerender.
  • Outer is a wrapper around inner. It has no meaning in the scope of this question and is only there to give props to Inner and to simulate prop changes.

To demonstrate the desired functionality I have implemented Inner with a class based component. A live version of this code can be found on codesandbox. How can I migrate it to a functional component:

Inner.tsx:

import React, { Component } from 'react'

interface InnerProps{outerNum:number}
interface InnerState{innerNum:number}

export default class Inner extends Component<InnerProps, InnerState> {
    state = {innerNum:0};

    shouldComponentUpdate(nextProps:InnerProps, nextState:InnerState){
        return this.props != nextProps;
    }
    render() {
        return (
            <button onClick={()=>{
                this.setState({innerNum: Math.floor(Math.random()*10)})
            }}>
                {`${this.props.outerNum}, ${this.state.innerNum}`}
            </button>
        )
    }
}

Outer.tsx:

import React, { useState } from "react";
import Inner from "./Inner";

export default function Outer() {
  const [outerState, setOuterState] = useState(1);

  return (
    <>
      <button
        onClick={() => {
          setOuterState(Math.floor(Math.random() * 10));
        }}
      >
        change outer state
      </button>
      <Inner outerNum={outerState}></Inner>
    </>
  );
}

The official docs say to wrap the component in React.memo. But this doesn't seem to work for preventing rerenders on state change. It only applies to prop changes.

I have tried to make React.memo work. You can see a version of the code with both Outer and Inner being functional components here.

Related questions:

How to use shouldComponentUpdate with React Hooks? : This question only deals with prop changes. The accepted answer advises to use React.memo

shouldComponentUpdate in function components : This question predates stateful functional components. The accepted answer explains how functional components don't need shouldComponentUpdate since they are stateless.

like image 677
lhk Avatar asked Nov 26 '22 23:11

lhk


2 Answers

React memo do not stop state changes

React.memo only checks for prop changes. If your function component wrapped in React.memo has a useState or useContext Hook in its implementation, it will still rerender when state or context change.

Ref:- https://reactjs.org/docs/react-api.html#reactmemo

like image 63
Code Maniac Avatar answered Dec 04 '22 08:12

Code Maniac


Your Inner component depends on the property num of the Outer component, you can't prevent it from rendering on property change as React.memo makes properties comparison:

// The default behaviour is shallow comparison between previous and current render properties.
const areEqual = (a, b) => a.num === b.num;
export default React.memo(Inner, areEqual);

By memoizing the Inner component and removing the num dependency, it won't render on Outer rendering, see sandbox attached.

export default function Outer() {
  const [outerState, setOuterState] = useState(1);

  return (
    <>
      ...
    // v Inner is memoized and won't render on `outerState` change.
      <Inner />
    </>
  );
}

Edit clever-silence-6xigt


If you want to implement shouldComponentUpdate with hooks you can try:

const [currState] = useState();
// shouldUpdateState your's custom function to compare and decide if update state needed
setState(prevState => {
  if(shouldUpdateState(prevState,currState)) {
    return currState;
  }
  return prevState;
});
like image 37
Dennis Vash Avatar answered Dec 04 '22 08:12

Dennis Vash