Search code examples
javascriptreactjsstatesetstate

Why does my react state update on component re render?


I have the following code:

const [number, setNumber] = useState(6)
function handleClick() { 
setNumber(5) 
}
...
<Button onClick={handleClick}/>

When I click the button, the component displays "5" as desired. But when I close the component and re-open it, it defaults back to "6". Does the set state happen on each re-render automatically? And is there a good way to work around this?


Solution

  • The issue is using local state versus global state.

    Unfortunately, global state is complicated in React as it needs to keep track of when to rerender.

    To do this, you could use useContext() and useReducer().

    This tutorial explains it well.

    Here is a working demo on codesandbox.io.

    1. Create a GlobalProvider.jsx file:

    import { createContext, useReducer } from "react";
    
    // Initial state
    const globalsInitialState = {
      number: 6
    };
    
    // Different actions to update the state
    const actions = {
      UPDATE_NUMBER: "UPDATE_NUMBER"
    };
    
    // How to update the state
    export const globalsReducer = (state, action) => {
      switch (action.type) {
        case actions.UPDATE_NUMBER:
          return {
            ...state,
            number: action.newNumber
          };
    
        default:
          return state;
      }
    };
    
    //Context and Provider
    export const GlobalsContext = createContext();
    
    export const GlobalsProvider = ({ children }) => {
      const [state, dispatch] = useReducer(globalsReducer, globalsInitialState);
    
      const value = {
        number: state.number,
        updateNumber: (newNumber) => {
          dispatch({ type: actions.UPDATE_NUMBER, newNumber });
        }
      };
    
      return (
        <GlobalsContext.Provider value={value}>{children}</GlobalsContext.Provider>
      );
    };
    

    2. Wrap your app with the Global Provider.

    This makes your global context available to all its children.

    import { GlobalsProvider } from "./GlobalsProvider";
    
    export default function App() {
      return (
        <GlobalsProvider>
          // ...
        </GlobalsProvider>
      );
    

    3. Use the GlobalContext in your component:

    import { useContext } from "react";
    import { GlobalsContext } from "./GlobalsProvider";
    
    export const Component1 = () => {
      const { number, updateNumber } = useContext(GlobalsContext);
    
      const handleClick = () => {
        updateNumber(5);
      };
    
      return (
        <div>
          <p>My number is {number}</p>
          <button onClick={handleClick}>Change number to 5</button>
        </div>
      );
    };