0

In my code, I have a provider component. In this component, I have state objects and some functions that change state. The problem is I want to change state depending on state value but in my functions, state stays the same as it was initialized.

Part of my code:

const AppContextProvider = () => {
  const [historyState, setHistoryState] = useState({
    historyLength: 0,
    currHistoryIdx: 0,
  });

  const historyAdd = () => {
    if (historyState.currHistoryIdx < historyState.historyLength)
      setHistoryState((state) => ({
        historyLength: state.currHistoryIdx + 1,
        currHistoryIdx: state.currHistoryIdx + 1,
      }));
    else
      setHistoryState((state) => ({
        historyLength: state.historyLength + 1,
        currHistoryIdx: state.historyLength + 1,
      }));
  };

  return (
    <AppContext.Provider
      value={{ state, centerState, rpState, historyState, functions }}
    >
      <App />
    </AppContext.Provider>
  );
};

I need to check the state to make a decision on how to change it, but in this function, the state always stays the same as it was initialized and because of this, my condition doesn't work properly.

I can get actual state only inside the setHistoryState function using the state parameter, but this solution doesn't suit me because I have more complex functions. Also if you use useEffect to check if the state changed, it will show the actual state in console.

What am I doing wrong and how can I fix it?

Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
Alyks
  • 41
  • 3
  • I don't know if this is can solve your problem because I have never set state on a Context component, but try to do this: setHistoryState({historyLength: historyState.historyLength + 1, currHistoryIdx: historyState.currHistoryIdx + 1}). As I said, I don't know if this can help you to fix the problem, if not just let me know and I will try coding a solution. – Victor Molina Aug 25 '20 at 21:23

1 Answers1

0

The state updater function can be used as such, a function, with any additional side-effect-free logic.

Move the condition inside the setHistoryState updater function:

const historyAdd = () => {
  setHistoryState((state) => {
    // here, state will always be up-to-date.
    if (state.currHistoryIdx < state.historyLength) {
      return {
        historyLength: state.currHistoryIdx + 1,
        currHistoryIdx: state.currHistoryIdx + 1,
      };
    }
    return {
      historyLength: state.historyLength + 1,
      currHistoryIdx: state.historyLength + 1,
    };
  });
};

I can get actual state only inside setHistoryState functions using state parameter, but this solution doesn't suit to me because I have more complex functions.

No matter the use-case or the complexity, using the latest state inside the updater function is likely the solution to this problem.

It can also be used in conjunction with useEffect to trigger changes, etc.

As an example, here's a more complex situation which is solved with the same solution.

Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129