Brandon Durham
Brandon Durham

Reputation: 7727

useCallback with state dependency causes infinite loop

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?

Upvotes: 1

Views: 1226

Answers (1)

Drew Reese
Drew Reese

Reputation: 203542

I don't think you need the dependency array populated at all, you can access the panels state from the draft copy in the functional state updater function.

const onValidatePanel = useCallback(isValid => {
  updatePanels(draft => {
    const panelIndex = draft.validPanels.indexOf(draft.activePanel);

    if (panelIndex === -1 && isValid) {
      // Add
      draft.validPanels.push(draft.activePanel);
    } else if (panelIndex > -1 && !isValid) {
      // Remove
      draft.validPanels.splice(panelIndex, 1);
    }
  });
}, []);

Upvotes: 1

Related Questions