Home > Net >  How to prevent extra re-render of child components of a function component react?
How to prevent extra re-render of child components of a function component react?

Time:06-20

I have created a simple Reactjs application. It uses two components a CounterButton and a CounterDisplay. Both are function components.

Both these components are used in another function component FunctionComponent which maintains the state of counter. The problem is that whenever I click on the CounterButton it renders the button again.

In contrast I created another component, ClassComponent which is a class component equivalent to FunctionComponent, and it does not re-render the button on each click.

I understand that the extra render is because I'm using arrow function clickHandler in FunctionComponent, but don't know how to fix this.

import React from 'react';

const CounterButton = React.memo((props) => {
  console.log(`${props.name} Counter Button Rendered`);
  return (<>
    <button onClick={props.onClick}>{props.name}: Click</button>
    </>)
});
const CounterDisplay = React.memo((props) => {
  console.log('Counter Display Rendered');
  return (<>
    <div>{props.name} : {props.counter}</div>
    </>)
});

function FunctionComponent() {
  const [counter, setCounter] = React.useState(0);

  var clickHandler = () => {
    console.log(">>> FunctionComponent: button clicked <<< ")
    setCounter(counter   1);
  };
  console.log('---FunctionComponent render---')
  return <>
    <CounterButton name="FunctionComponent" onClick={clickHandler} />
    <CounterDisplay name="FunctionComponent" counter = {counter} />
  </>
}

class ClassComponent extends React.Component {
  state = {
    counter: 0
  };

  clickHandler = () => {
    console.log(">>> ClassComponent: button clicked <<< ")
    this.setState(prev => ({counter: prev.counter   1}));
  };
  render() {   
    console.log('---ClassComponent render---')
    return <>
      <CounterButton name="ClassComponent" onClick={this.clickHandler} />
      <CounterDisplay name= "ClassComponent" counter = {this.state.counter} />
    </>
  }
}

function App() {
  return <>
    <FunctionComponent/>
    <ClassComponent/>
  </>
}
export default App;

Application starts and I see all components rendered once

---FunctionComponent render---
FunctionComponent Counter Button Rendered
Counter Display Rendered
---ClassComponent render---
ClassComponent Counter Button Rendered
Counter Display Rendered

When I click on the FunctionComponent's CounterButton react re-renders the button again.

>>> FunctionComponent: button clicked <<< 
---FunctionComponent render---
FunctionComponent Counter Button Rendered
Counter Display Rendered

When I click on the ClassComponent's CounterButton react does not re-render the button again.

>>> ClassComponent: button clicked <<< 
---ClassComponent render---
Counter Display Rendered

I tried using useCallBack for clickHandler, but it didn't change anything.

  var clickHandler = useCallback(() => {
    console.log(">>> FunctionComponent: button clicked <<< ")
    setCounter(counter   1);
  },[counter]);

How to achieve the same behavior in FunctionComponent i.e. not re-render button on each click?

CodePudding user response:

I tried using useCallBack for clickHandler, but it didn't change anything.

 var clickHandler = useCallback(() => {
   console.log(">>> FunctionComponent: button clicked <<< ")
   setCounter(counter   1);
 },[counter]);

The reason it didn't change anything is that you're creating a new click handler every time counter changes. Change your code to use the function version of setState, and remove counter from the dependency array:

const clickHandler = useCallback(() => {
  setCounter(prev => prev   1);
}, []);
  • Related