'Avoid children rerendering when passing a function in props

I have this parent component:

const MeasuresList: React.FC = () => {
    const root = useAppSelector(state => state.tree.root)
    const selected = useAppSelector(state => state.stats.selected)
    const dispatch = useAppDispatch()
    const {data, isFetching} = useGetMeasuresQuery(
        (root ? root.element.code : skipToken),
    )

    if (!data || isFetching)
        return null


    return <List dense disablePadding>
        {data.map(measure => {
            const isSelected = selected.indexOf(measure.code) !== -1

            return <MeasureSelectable
                key={measure.code}
                measure={measure}
                selected={isSelected}
                onChanged={() => dispatch(!isSelected ? addMeasure(measure.code) : removeMeasure(measure.code))}
            />
        })}
    </List>
}

which renders a list of:

const MeasureSelectable: React.FC<{
    measure: Measure,
    selected: boolean,
    onChanged: () => void,
}> = ({measure, selected, onChanged}) => {

    return <ListItem disablePadding>
        <ListItemButton dense disableGutters onClick={onChanged}>
            <ListItemIcon>
                <Checkbox
                    edge="start"
                    checked={selected}
                    disableRipple
                />
            </ListItemIcon>
            <ListItemText primary={measure.name}/>
        </ListItemButton>
    </ListItem>
}

The problem is that when I click on a checkbox on any child, the redux state changes (OK), the parent component gets rerendered (OK) but then every child gets rerendered because the function () => dispatch(...) is recreated each time causing the children rerendering, which is slow.

I would like that the function () => dispatch(...) is fixed (i.e. with useCallback, but I don't know how to use it since it has parameters which depend on each child), so that only the child with selected changed gets rerendered.

How could I do that? I cannot use useCallback inside the onChanged, I may define it at the beginning of the FC but then I'm not sure how to parametrize it (there should be a callbacked function for each key, so to speak)



Solution 1:[1]

You may expose measure in onChanged callback from MeasureSelectable like this: onChanged: (measure: Measure) => void and then use it in memoized function (the useCallback one) to determine if given element should be selected or not.

This way there will be only one instance of this function and props of MeasureSelectable will stay stable all the time.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Aitwar