Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React createContext/useContext does not survive between pages

I am trying to create a shared global state for all components that an app needs, and instead of relying on props drilling or redux, I am trying to achieve that with the React Context.

Why does my user context not survive when I switch between routes? The application bellow illustrates the issue.

Do I need to use any other hook in conjunction with useContext?

//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { AuthenticationProvider }  from "./AuthenticationProvider";

const Index = () => {
    return (
      <AuthenticationProvider>
        <App />
      </AuthenticationProvider>
    );
}

ReactDOM.render(<Index />, document.getElementById('root'));
//App.js
import React, { useState, useContext } from 'react';
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import './App.css';
import { AuthenticationContext } from './AuthenticationProvider';

function AddUser() {

  const [formUser, setFormUser] = useState("");

  const [user, setUser] = useContext(AuthenticationContext);

  const handleSubmit = async (event) => {
    event.preventDefault();
    setUser(formUser);
  }

  return (
    <React.Fragment>
      Form user: {formUser}.
          <form id="form1" onSubmit={handleSubmit}>
        <input type="text" id="user" onChange={event => setFormUser(event.target.value)} />
        <input type="submit" value="Save" />
      </form>
      <br/>
      Current user: {user}
      <br/>
      <a href="/">Back to home</a>
    </React.Fragment>
  );
}

function Home() {

  const [user, setUser] = useContext(AuthenticationContext);

  return (
    <React.Fragment>
      <div className="App">
        Hello {user}.
        <br/>
        <a href="/add">Add user</a>
    </div>
    </React.Fragment>
  );
}

function App() {
  return (
    <Router>
      <Switch>
        <Route exact path="/" component={Home} />
        <Route exact path="/add" component={AddUser} />
      </Switch>
    </Router>
  );
}

export default App;

//AuthenticationProvider.js
import React, { useState, createContext } from "react";

const DEFAULT_STATE = "";

export const AuthenticationContext = createContext(DEFAULT_STATE);

export const AuthenticationProvider = ({ children }) => {
  const [user, setUser] = useState(DEFAULT_STATE);

  return (
    <AuthenticationContext.Provider value={[user, setUser]} >
      {children}
    </AuthenticationContext.Provider>
  );
}
like image 476
Pablo Avatar asked Jun 15 '19 08:06

Pablo


1 Answers

The problem is that you used a regular <a> link to navigate through the app and every time you go from Home to addUser the app refreshes. To navigate through the app without refreshing the page use the Link component from react-router-dom

in Home and AddUser change the a links to the Link component

import { Link } from "react-router-dom";

function Home() {
  const { user, setUser } = useContext(AuthenticationContext);

  return (
    <React.Fragment>
      <div className="App">
        Hello {user}.
        <br />
        <Link to="/add">Add user</Link> <-- Changed a to Link
      </div>
    </React.Fragment>
  );
}

function AddUser() {

  const [formUser, setFormUser] = useState("");

  const [user, setUser] = useContext(AuthenticationContext);

  const handleSubmit = async (event) => {
    event.preventDefault();
    setUser(formUser);
  }

  return (
    <React.Fragment>
      Form user: {formUser}.
          <form id="form1" onSubmit={handleSubmit}>
        <input type="text" id="user" onChange={event => setFormUser(event.target.value)} />
        <input type="submit" value="Save" />
      </form>
      <br />
      Current user: {user}
      <br />
      <Link to="/">Back to home</Link> <-- Changed a to Link
    </React.Fragment>
  );
}
like image 72
Asaf Aviv Avatar answered Nov 15 '22 03:11

Asaf Aviv