Search code examples
reactjsreact-context

How to use React Context API to have a state across multiple Routes?


I'm trying to understand how the context API works. I'd like to keep a global state that I can update from any Class Component. Currently, when I try to update my Context using the provided function, It only updates the value locally. In my code, I try to update a field "Day" into "Hello", and the change can be seen only when Writer is rendered. As soon as I ask my browser to render "Reader", the value is "Day" again. Why does this happen? Here's my code, I simplified it as much as I could:

index.js:
    import React from "react";
    import ReactDOM from "react-dom";
    import {ThemeContextProvider} from "./ThemeContext";
    
    import App from "./App";
    
    ReactDOM.render(
    <ThemeContextProvider>
       <App />
    </ThemeContextProvider>,
  document.getElementById("root")
);
app.js:
import React from "react";
import Writer from "./Writer.js";
import Reader from "./Reader.js";
import { Context } from "./ThemeContext.js";
import {BrowserRouter as Router, Switch, Route} from 'react-router-dom';

class App extends React.Component {
  static contextType = Context;
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <div className="app">
        <Router>
          <Switch>
            <Route path="/writer" component={Writer}></Route>
            <Route path="/reader" component={Reader}></Route>      
          </Switch>
        </Router>
      </div>
    );
  }
}

export default App;
context.js:
import React, { Component } from "react";
const Context = React.createContext();
const { Provider, Consumer } = Context

// Note: You could also use hooks to provide state and convert this into a functional component.
class ThemeContextProvider extends Component {
  state = {
    theme: "Day"
  };

  setTheme = (newTheme) => {
    this.setState({theme: newTheme})
  };

  render() {
    return <Provider value={{theme: this.state.theme, setTheme: this.setTheme}}>{this.props.children}</Provider>;
  }
}

export { ThemeContextProvider, Consumer as ThemeContextConsumer, Context };
writer.js:
import React from "react";
import {Context} from "./ThemeContext";

class Writer extends React.Component {
  static contextType = Context
  constructor(props) {
    super(props);
    this.write = this.write.bind(this)
  }

  write () {
    this.context.setTheme("hello")
  }

  render() {
    return (
      <div>
        <button onClick={this.write}>press</button>
        <p>{this.context.theme}</p> 
      </div>
    );
  }
}

export default Writer;
reader.js:
import React from "react";
import { Context, ThemeContextConsumer } from "./ThemeContext";

class Reader extends React.Component {
  static contextType = Context;
  constructor(props) {
    super(props);
  }
  render () {
    return(
          <div>
            <p>{this.context.theme}</p>
          </div>
  );}
}

export default Reader;

Solution

  • how do you handle the maneuver to different pages? If right now, you handle it manually by typing it directly in the search top browser input placeholder. Then it will not work since the page getting refresh. Using just context api will not make your data persistant. You need to incorporate the use of some kind of storage to make it persistant.

    Anyhow, your code should work if there's not page refresh happen. To see it in different pages tho, you can and a Link (from react-router-dom package) or basically a button to redirect you to different pages, like so:-

    • just add this in your Writer.js component for testing purposes:-
    import React from "react";
    import { Link } from 'react-router-dom'
    import {Context} from "./ThemeContext";
    
    class Writer extends React.Component {
      static contextType = Context
      constructor(props) {
        super(props);
        this.write = this.write.bind(this)
      }
    
      write () {
        this.context.setTheme("hello")
      }
    
      render() {
        return (
          <div>
            <button onClick={this.write}>press</button>
            <p>{this.context.theme}</p> 
            <Link to="/reader">Go to Reader page</Link>
          </div>
        );
      }
    }
    
    export default Writer;