Reputation: 3032
I have a simple App component
export default function App() {
const [count, changeCount] = useState(0)
const onIncreaseClick = useCallback(() => {
changeCount(count + 1)
}, [count])
const onPress = useCallback(() => {
alert('pressed')
}, [])
return (<>
<button onClick={onIncreaseClick}>Increase</button>
<ButtonPressMe onClick={onPress} />
</>);
}
I expect that onPress variable contains always the same link since parameters never change
And i expect that my ButtonPressMe component will be rendered just once - with the first App component rendering... because it has just one prop and value of this prop never change... therefore no need to rerender component. Correct?
Inside my ButtonPressMe component i check it with console.log
const ButtonPressMe = ({ onClick }) => {
console.log('Button press Me render')
return <button onClick={onClick}>Press me</button>
}
And against my expectations it rerenders each time when parent component rerenders after Increase button is pressed.
Did i misunderstood something?
Upvotes: 1
Views: 83
Reputation: 29282
By default, when a parent component re-renders, all of its child components re-render too.
useCallback
hook will preserve the identity of the onPress
function but that won't prevent a re-render of the ButtonPressMe
component. To prevent a re-render, React.memo()
is used. useCallback
hook is used to avoid passing a new reference to a function, as a prop to a child component, each time a parent component re-renders.
In your case, combination of React.memo
and useCallback
hook will prevent a re-render of ButtonPressMe
component.
function App() {
const [count, changeCount] = React.useState(0);
const onIncreaseClick = React.useCallback(() => {
changeCount(count + 1);
}, [count]);
const onPress = React.useCallback(() => {
alert("pressed");
}, []);
return (
<div>
<button onClick={onIncreaseClick}>Increase</button>
<ButtonPressMe onClick={onPress} />
</div>
);
}
const ButtonPressMe = React.memo(({ onClick }) => {
console.log("Button press Me render");
return <button onClick={onClick}>Press me</button>;
});
ReactDOM.render(<App/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Upvotes: 1
Reputation: 73
The default behavior in React is to change everything in the App when anything changes, in your case you're changing the state of the parent of your custom button, therefore React re-renders everything including your button.
You can find an explanation on how React decides to re-render components here: https://lucybain.com/blog/2017/react-js-when-to-rerender/#:~:text=A%20re%2Drender%20can%20only,should%20re%2Drender%20the%20component.
Upvotes: 0
Reputation: 85032
And against my expectations it rerenders each time when parent component rerenders after Increase button is pressed.
Did i misunderstood something?
That's the default behavior in react: when a component renders, all of its children render too. If you want the component to compare its old and new props and skip rendering if they didn't change, you need to add React.memo to the child:
const ButtonPressMe = React.memo(({ onClick }) => {
return <button onClick={onClick}>Press me</button>
})
Upvotes: 1