Home > Software engineering >  How to stop infinite loop when setting initial state from API with useReducer (React Hooks)
How to stop infinite loop when setting initial state from API with useReducer (React Hooks)

Time:03-07

I am trying to set my initial state via useReducer. This data comes from an API, so I have placed it within useEffect and applied a dispatch method. (I need to use useReducer because I have more complex logic to come later.)This works however it causes an infinite number of calls to the API and if I console log the state, it just loops draining the memory.

For reference, when I log with eg:

const { state, dispatch } = useContext(CanvasContext);
// console.log(state);

I would just like it to run once on load, however when I add specific values to the useEffect array it doesn't help. Is there a way to stop the infinite loop? I have tried to use a check if mounted approach (as recommended by other answers, but don't really understand what I should be doing with it).

Thank you.

export const CanvasContext = React.createContext([]);

function cartReducer(state, action) {
  switch (action.type) {
    case "INITIALIZE":
      return action.payload;
    default:
      return state;
  }
}

const initialState = {
  initialized: false,
  robots: [],
  cart: [],
};

export const CanvasProvider = ({ children }) => {
  const [state, dispatch] = useReducer(cartReducer, initialState);

  useEffect(() => {
    getStoredCanvas().then((response) => {
      dispatch({
        type: "INITIALIZE",
        payload: {
          ...initialState,
          robots: response?.data.elements,
          initialized: true,
        },
      });
    });
  }, []);

  return (
    <>
      {state.robots.length && (
        <CanvasContext.Provider value={{ state, dispatch }}>
          {children}
        </CanvasContext.Provider>
      )}
    </>
  );
};
// getStoredCanvas
export async function getStoredCanvas() {
  let canvas;
  try {
    if (useLocal) {
      canvas = axios.get(endPoints.local);
    } else {
      canvas = axios.post(endPoints.remote, testLoadCanvas);
      // .then((response) => console.log(response));
    }
    return canvas;
  } catch (error) {
    console.error(error);
  }
}

CodePudding user response:

Looks like you're throwing around an initialized variable, but never using it. Seems like that variable is only ever relevant whenever the provider is mounted. With that being said, I would move the variable into a state variable and use it to ensure the initialization process only ever happens once:

export const CanvasProvider = ({ children }) => {
  const [state, dispatch] = useReducer(cartReducer, initialState);
  const [initialized, setInitialized] = useState(false)

  useEffect(() => {
    if(initialized) return 
    getStoredCanvas().then((response) => {
      setInitialized(true)
      dispatch({
        type: "INITIALIZE",
        payload: {
          ...initialState,
          robots: response?.data.elements,
        },
      });
    });
  }, [dispatch, initialized]);

  return (
    <>
      {state.robots.length && (
        <CanvasContext.Provider value={{ state, dispatch }}>
          {children}
        </CanvasContext.Provider>
      )}
    </>
  );
};
  • Related