I’ve got a useCallback that updates state, but because it requires that state as a dependency, it creates an infinite loop when it updates the state. I’m using useImmer here, but it happens when using plain useState, too.
const [panels, updatePanels] = useImmer({
activePanel: 0,
validPanels: [],
});
const onValidatePanel = useCallback(isValid => {
const panelIndex = panels.validPanels.indexOf(panels.activePanel);
// Add
if (panelIndex === -1 && isValid) {
updatePanels(draft => {
draft.validPanels.push(draft.activePanel);
});
// Remove
} else if (panelIndex > -1 && !isValid) {
updatePanels(draft => {
draft.validPanels.splice(panelIndex, 1);
});
}
}, [panels]);
Basically when an index is added or removed, panels changes, triggers onValidatePanel again, and re-adds the index, and on and on…
How might I work around this?
panelsstate into separateactivePanelandvalidPanelsstate or don't tagpanelsas a dependency of your callback. I would prefer the former.onValidatePanelcallback is hooked up? Because changingpanelswill just rebuild the callback but it won't actually trigger it...panelsstate from draft in the functional update you could completely eliminate the need forpanelsas a dependency and eliminate the dependency cycle.setState. You can receive previous state as an arg rather than specify it as a dependency. (reactjs.org/docs/hooks-reference.html#functional-updates)useCallbackshould not by itself trigger an infinite loop. That's only going to happen if you have it in the dependency array of auseEffect, and call it unguarded from within that effect. I presume you have one but have not included it? The functional state update is probably the right way to go anyway, but something else to consider.