Hayi
Hayi

Reputation: 6236

Issues with useReducer not synchronously updating the state

According to React docs :

useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one.

1. can somebody explain me why useReducer is not updating the state synchronously ?

const reducer = (state, action) => {
    if( action.type === 'ADD_VALUE') {
        console.log(`STATE IN REDUCER`, [...state, action.path]) // => ["1.1"]
        return [...state, action.path]
    }   
}

const [state, dispatch] = useReducer(reducer, [])

<input type="button" onClick={() => {
    dispatch({ type: 'ADD_VALUE', path: "1.1"})
    console.log(`STATE`, state) // => []
    // here i want to do some stuff based on the lastest updated state (["1.1"] and not [])
    // for example dispatch an action with redux
}}/>

2. How can I do some stuff (dispatch a redux action) based on the lastest updated state (["1.1"] and not []) ?

Upvotes: 14

Views: 18172

Answers (1)

Win
Win

Reputation: 5584

Use useEffect to access the state correctly. You could add some safe-guarding if you want something invoking if a certain criterion is hit.

If you want to access your reducer across components, you can store the reducer using Context API. Look below for an example. You can see the reducer being injected into the Context on the parent component and then two child components that a) dispatches an action b) receives the update from the action.

1. Example of context reducer to use across multiple components

import React from "react";
import ReactDOM from "react-dom";

const Application = React.createContext({
  state: null,
  dispatch: null
});

function ActionComponent() {
  const { dispatch } = React.useContext(Application);
  return (
      <div>
          <div>Action Component</div>
          <button onClick={() => dispatch("lol")}>Do something</button>
      </div>
  );
}

function ListenerComponent() {
  const { state } = React.useContext(Application);
  React.useEffect(
    () => {
      console.log(state);
    },
    [state]
  );
  return <div>Listener Component</div>;
}

function App() {
  const [state, dispatch] = React.useReducer(function(state = [], action) {
    return [...state, action];
  });
  return (
    <Application.Provider value={{ state, dispatch }}>
      <div className="App">
        <ActionComponent />
        <ListenerComponent />
      </div>
    </Application.Provider>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

2. Example of local reducer without using Application Context

const reducer = (state, action) => {
    if( action.type === 'ADD_VALUE') {
        return [...state, action.path]
    }   
}

const [state, dispatch] = useReducer(reducer, [])

React.useEffect(() => {
    console.log(state);
}, [state]);

<input type="button" onClick={() => {
    dispatch({ type: 'ADD_VALUE', path: "1.1"})
}}/>

Upvotes: 13

Related Questions