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);
}, []);