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?
Working CodesandboxWhat 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.
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 references stay the same.