skip to Main Content

I pass the click handler to the child component and it causes re-rendering every time when a parent component changes. Can’t figure out how to stop it. Without the "handleClick" prop the child component doesn’t re-render.

function SubComponent({ handleClick }) {
  console.log("component render");
  return <div onClick={handleClick}>SubComponent</div>;
}

const MemoizedSubComponent = memo(SubComponent);

export default function App() {
  console.log('app render')
  const [test, setTest] = useState(1);
  const onHandleClick = () => {
    console.log(1)
};
const increment = ()=>{
  setTest(test+1)
};
  return (
    <><button onClick={increment}>Increment {test}</button>
    <MemoizedSubComponent
        handleClick={onHandleClick}
    /></>
);
}

Working code is here: https://codesandbox.io/s/zen-https-6hl04z

How to test:

  • click the "IncrementN" button in the App component and see the console: it will say "rerender component" every time when you click.
  • if you remove or comment the handleClick={onHandleClick} line in the App, there is no re-rendering every time but only when some data props change like tab.

How to prevent this re-rendering?

2

Answers


  1. React will re-render your component when it detects changes in props.

    You have defined a function testoClick in App, and passed it to TodoList.

    testoClick is created every time App renders, and so React detects that props has changed, and will re-render TodoList. This is expected.

    You can use the useCallback hook to memoise the testoClick handler. useCallback will return the same object reference so long as the dependencies have not changed.

    By returning a memoized testoClick, React will not detect any changes in props, and therefore will not re-render TodoList

    const testoClick = React.useCallback(() => {
        console.log(1);
      },[]);
    
    Login or Signup to reply.
  2. Since you are using an arrow method directly, the method is not memoized. This would create a new instance every time the component is rendered (with new props object with a new reference even though it looks the same on code).

    Wrapping the testoClick method with an useCallback hook would help resolve your problem.

    const testoClick = useCallback(() => {
        console.log(1);
    }, []);
    

    Tip for optimization: Move all the inline arrow methods to useCallback wrapped methods. Example below:

    const someMethod = useCallback(() => setTest(test + 1)), [test])
    
    <button onClick={someMethod}>Increment{test}</button>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search