'Why isn't my React context being updated?

When I use the app and navigate to the SetContext component, I see the value of auth is correct ("dummy value") and after entering a value in the edit field and pressing the button I see the setAuth method trigger in the App component and then I also see the auth changes in the useEffect method of the App component (I just put that in to see if it registered the update). That would be fantastic except that then navigating to any page the value of auth reverts to the original value ("dummy value") and I can't determine why.

Here are my versions of the React libs in use:

    "react": "^18.1.0",
    "react-dom": "^18.1.0",
    "react-router": "^6.3.0",
    "react-router-dom": "^6.3.0",

Here is my index.js (it is purely boilerplate but I'll post just to prove it):

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: <edited per stack overflow editor requirements>;

Here is my App.js:

import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import './App.css';
import { AuthProvider } from './components/AuthContext';
import Navbar from './components/Navbar';
import Home from './components/Home';
import About from './components/About';
import SetContext from './components/SetContext';

function App() {
  const [auth, setAuth] = React.useState("dummy value");

  React.useEffect(() => {
    console.log("auth updated to: "+auth);
  }, [auth]);

  const changeAuth = (value) => {
    setAuth(value);
  }

  return (
    <>
      <AuthProvider value={auth}>
        <Router>
          <Navbar />
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/home" element={<Home />} />
            <Route path="/about" element={<About />} />
            <Route path="/setcontext" element={<SetContext setAuth={changeAuth} />} />
          </Routes>
        </Router>
        <footer>This is the footer</footer>
      </AuthProvider>
    </>
  );
}

export default App;

And finally here is my SetContext.js:

import React from 'react';
import AuthContext from './AuthContext';

const SetContext = ( { setAuth } ) => {
    const auth = React.useContext(AuthContext);

    const changeHandler = (event) => {
        const value = document.getElementById("newContext").value;
        setAuth(value);
    }

    return (
        <>
            <div>This is the SetContext Component The auth context value is {auth}</div>
            <div><input id="newContext"></input></div>
            <div><button onClick={changeHandler}>Set Context</button></div>
        </>
    );
}

export default SetContext;


Solution 1:[1]

From what I can tell you are correctly updating the Context value. This issue comes when you are accessing the Context value. The Context value is just the auth state value, which is a string type.

const [auth, setAuth] = React.useState("dummy value");

...

return (
  <AuthProvider value={auth}> // <-- context value is auth
    ...
  </AuthProvider>
);

When accessing the Context value the code is incorrectly attempting to destructure it as if it were an object.

const { auth } = React.useContext(AuthContext);

The consumers should just take the return value.

const auth = React.useContext(AuthContext);

Edit why-isnt-my-react-context-being-updated

Solution 2:[2]

I am suggesting to you use other structure , create a new file for example AuthContext.js , with this structure

import React, {useState, useContext} from 'react';

const AuthContext = React.createContext();

export const useAuthContext = () => {
    return useContext(AuthContext);
}

export const AuthProvider = ({children}) => {
    const [auth, setAuth] = React.useState("dummy value");
    const state = {
        auth,
        setAuth
    }
   return (
       <AuthContext.Provider value={state}>
            {children}
        </AuthContext.Provider>
   );
}

in the index.js don't pass value , whole DOM inside of AuthProvider will be your children prop in the AuthContext.js

   <AuthProvider>
        <Router>
          <Navbar />
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/home" element={<Home />} />
            <Route path="/about" element={<About />} />
            <Route path="/setcontext" element={<SetContext />} />
          </Routes>
        </Router>
        <footer>This is the footer</footer>
      </AuthProvider>

and use this AuthContext on SetContext.js like this , without any prop , because setAuth will be state of your context

import {useAuthContext} from './AuthContext';

const SetContext = () => {
    const { auth ,setAuth} = useAuthContext();
    const changeHandler = (event) => {
        const value = document.getElementById("newContext").value;
        setAuth(value);
    }

    return (
        <>
            <div>This is the SetContext Component The auth context value is {auth}</div>
            <div><input id="newContext"></input></div>
            <div><button onClick={changeHandler}>Set Context</button></div>
        </>
    );
}

usage of value={auth} way isn't good practice Caveats

in the App.js , useEffect dependency can be context auth

const { auth} = useAuthContext();

  React.useEffect(() => {
    console.log("auth updated to: "+auth);
  }, [auth]);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Drew Reese
Solution 2