I want to avoid re-render of my child component <ChildComponent/>
whenever I update my state using a onClick in <ChildComponent/>
.
I have my callback function in <ParentComponent/>
which updates one of the values for the key-value pair object.
In the parent component
const _keyValueObject = useMemo(() => utilityFunction(array, object), [array, object])
const [keyValueObject, setKeyValueObject] = useState<SomeTransport>(_keyValueObject)
const handleStateChange = useCallback((id: number) => {
setKeyValueObject(keyValueObject => {
const temp = { ... keyValueObject }
keyValueObject[id].isChecked = ! keyValueObject[id].isChecked
return temp
})
}, [])
return(
<Container>
{!! keyValueObject &&
Object.values(keyValueObject).map(value => (
<ValueItem
key={value.id}
category={value}
handleStateChange ={handleStateChange}
/>
))}
</Container>
)
In child component ValueItem
const clickHandler = useCallback(
event => {
event.preventDefault()
event.stopPropagation()
handleStateChange(value.id)
},
[handleStateChange, value.id],
)
return (
<Container>
<CheckBox checked={value.isChecked} onClick={clickHandler}>
{value.isChecked && <Icon as={CheckboxCheckedIcon as AnyStyledComponent} />}
</CheckBox>
<CategoryItem key={value.id}>{value.title}</CategoryItem>
</Container>
)
export default ValueItem
In child component if I use export default memo(ValueItem), then the checkbox does not get updated on the click.
What I need now is to not re-render every child component, but keeping in mind that the checkbox works. Any suggestions?
CodePudding user response:
Working Codesandbox
Explanation
What you need to do is wrap the child with React.memo
. This way you ensure that Child is memoized and doesn't re-render unnecessarily. However, that is not enough.
In parent, handleStateChange
is getting a new reference on every render, therefore it makes the parent render. If the parent renders, all the children will re-render. Wrapping the handleStateChange
with useCallback
makes sure react component remembers the reference to the function. And memo
remembers the result for Child
.
Useful resource
CodePudding user response:
Spreading (const temp = { ... keyValueObject }
) doesn't deep clone the object as you might think. So while keyValueObject
will have a new reference, it's object values will not be cloned, so will have the same reference, so memo
will think nothing changes when comparing the category
prop.
Solution: make sure you create a new value for the keyValueObject
's id which you want to update. Example: setKeyValueObject(keyValueObject => ({...keyValueObject, [id]: {...keyValueObject[id], isChecked: !keyValueObject[id].isChecked}))
. Now keyValueObject[id]
is a new object/reference, so memo
will see that and render your component. It will not render the other children since their reference stays the same.