I am trying to figure out why when I click on a specific ponent its sibling it will render too
function CountButton({increment, count, number}) {
console.log(`Render CountButton ${number}`)
return <button onClick={() => increment(count + 1)}>{count}</button>
}
function DualCounter() {
const [count1, setCount1] = React.useState(0)
const increment1 = React.useCallback(() => setCount1(c => c + 1), [])
const [count2, setCount2] = React.useState(0)
const increment2 = React.useCallback(() => setCount2(c => c + 1), [])
console.log('Render DualCounter')
return (
<>
<CountButton count={count1} increment={increment1} number={1} />
<CountButton count={count2} increment={increment2} number={2} />
</>
)
}
I use useCallback
and I pass theses function to use avoid that in any render the functions reference will be a different reference.
I am trying to figure out why when I click on a specific ponent its sibling it will render too
function CountButton({increment, count, number}) {
console.log(`Render CountButton ${number}`)
return <button onClick={() => increment(count + 1)}>{count}</button>
}
function DualCounter() {
const [count1, setCount1] = React.useState(0)
const increment1 = React.useCallback(() => setCount1(c => c + 1), [])
const [count2, setCount2] = React.useState(0)
const increment2 = React.useCallback(() => setCount2(c => c + 1), [])
console.log('Render DualCounter')
return (
<>
<CountButton count={count1} increment={increment1} number={1} />
<CountButton count={count2} increment={increment2} number={2} />
</>
)
}
I use useCallback
and I pass theses function to use avoid that in any render the functions reference will be a different reference.
- Because you pass in count1 / count2 as a prop as well. This props is changed so the ponent is re-rendered – Kevin Commented Feb 21, 2021 at 19:26
- If DualCounter is re-renders, it's children will render too.. You can use memo – lissettdm Commented Feb 21, 2021 at 19:29
1 Answer
Reset to default 5You are seeing a re-render on the sibling <CountButton />
ponent, because each time you hit the button to update the counter, you are actually updating a state value in the parent ponent <DualCounter />
, which causes a re-render on that ponent as well.
And since DualCounter
is re-rendered, child ponents will re-render as well, which in this case includes both <CountButton />
elements.
A solution to prevent this, would be wrapping CountButton ponent with React.memo(). This will prevent a re-render on a ponent that didn't have any change on the props values.
Example below:
function CountButton({increment, count, number}) {
console.log(`Render CountButton ${number}`)
return <button onClick={() => increment(count + 1)}>{count}</button>
}
const CountButtonMemo = React.memo(CountButton)
function DualCounter() {
const [count1, setCount1] = React.useState(0)
const increment1 = React.useCallback(() => setCount1(c => c + 1), [])
const [count2, setCount2] = React.useState(0)
const increment2 = React.useCallback(() => setCount2(c => c + 1), [])
console.log('Render DualCounter')
return (
<>
<CountButtonMemo count={count1} increment={increment1} number={1} />
<CountButtonMemo count={count2} increment={increment2} number={2} />
</>
)
Another solution would be not updating the DualCounter
state on each change caused by events on your CountButton
ponents, which will stop triggering unwanted re-renders on their siblings. You could handle the state directly on each CountButton ponent if this made sense for your app.
Alternatively, you could use a React state management tool, such as Redux, which also solves exactly this issue, by taking charge of delegating the state of your app separated from your ponents themselves.