2

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;
Tim Manchester
  • 137
  • 1
  • 3
  • 16

2 Answers2

2

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

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • Thanks for that Drew, that solved a bug. Now after you enter a value in the input and press the button on the SetContext component, it shows the correct value in the div where the context value is displayed. However I still revert to the previous value as soon as I go to another component. Is there something wiggy in the react-router-dom? – Tim Manchester May 04 '22 at 04:04
  • @TimManchester Can you clarify what you mean with "However I still revert to the previous value as soon as I go to another component"? The codesandbox I included doesn't seem to act in that way, the current context value is accessible in all the routed components. – Drew Reese May 04 '22 at 04:57
  • Hey @Drew, sure thing. First I put the project up on GitHub so you can see all the nooks and crannies: https://github.com/dirkwiggley/mui-test.git . Then here's how to replicate the issue: 1. After launching the app go to http://localhost:3000/home and you'll see the original value displayed (maybe put some breakpoints where you want to see things change), then go to http://localhost:3000/setcontext, put some other value in the input field and click the button. Then go back to http://localhost:3000/home and you'll see the original value is being displayed again. Also note the value in tools. – Tim Manchester May 04 '22 at 17:03
  • @TimManchester Did you forget to push your code to your repo? It's only 3 files. – Drew Reese May 04 '22 at 17:10
  • well that's just embarrassing. Sorry. It's all there now: https://github.com/dirkwiggley/mui-test.git – Tim Manchester May 04 '22 at 21:49
  • @TimManchester No worries, mistakes happen. I'll check when I have availability. – Drew Reese May 04 '22 at 21:51
  • @TimManchester Were you ever able to update your repo? I see it's no longer available. FWIW I'm still unable to reproduce the issue you describe in my provided sandbox code. Have you tried applying the code/logic using there to your *actual* code? What are the steps to reproduce the issue as you see it? – Drew Reese May 10 '22 at 22:40
  • Howdy, I don’t know how I managed to Turn the project private. It’s public again. Anyway, I’ve followed the basics of context. Work really wrecked me today, I’ll compare my code to yours tomorrow. Thanks for posting. – Tim Manchester May 11 '22 at 04:02
  • 1
    @TimManchester, I notice you aren't rendering any links in your app. How exactly are you navigating around to test that the React context value "sticks"? – Drew Reese May 11 '22 at 05:15
  • I originally had a lot more in another app when I noticed that context didn't work. After trying a bunch of stuff I just wrote an app with only context that did work and a bit at a time put parts of my original app back in until context broke so there's undoubtedly some things that might look off but I didn't want to keep putting stuff back in after I got to where context broke. That said, I just update the url in the address bar and hit enter. It's stupid but I'm just trying to demo the broken bit. – Tim Manchester May 11 '22 at 23:26
  • 1
    @TimManchester That's what I suspected was the case. When you just update the URL in the address bar manually it will reload the entire app, which wipes React state. In other words, the page is reloaded and the entire app mounted again. My sandbox code uses links to navigate to the other pages and the context component remains mounted and keeps the state. – Drew Reese May 11 '22 at 23:38
  • Well heckfire, I didn't see this comment and awarded the bounty to the fellow below because when I followed those directions I decided to add links to make it a bit easier to test. Your answer is the most appropriate but I can't undo the award. I am deeply sorry. – Tim Manchester May 12 '22 at 00:03
  • 1
    @TimManchester No worries, there are other ways to show appreciation if answers were helpful/useful. Cheers and good luck! – Drew Reese May 12 '22 at 01:40
1

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]);
Hakob Sargsyan
  • 1,294
  • 1
  • 9
  • 15
  • Wow, there's a bit to digest there but I followed your instructions and it now works. I just wish I understood why the earlier version was not working. – Tim Manchester May 12 '22 at 00:01
  • You were change state of App.js , auth was App.js incapsuled state , when you define state on context state , it will be global for every route and you won't be lost it , like a global variable it will be available until next context Re render @TimManchester – Hakob Sargsyan May 12 '22 at 05:04