Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to share application State between React Router v4 routes

I would like to know what kind of solutions I have to solve the following issue:

I don't use any state mangement library. I "only use" React.

The global application state is stored in the component <MyFoodApp/>. It contains an array of restaurant objects.

The component RestaurantPage is used to display the details of a restaurant and allows to create a new review.

At this point I'm stuck because I need to update a specific restaurant object stored in the state of <MyFoodApp /> in order to add this submitted review.

I used to update the state via functions passed as props to the children components but in this case I can't because MyFoodApp is not a parent of RestaurantPage.

Do I have to set up Redux to fix this issue or is there any workarounds ?

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Link, Route, Switch } from 'react-router-dom';

import EasyFoodApp from './js/components/EasyFoodApp';
import RestaurantPage from './js/components/RestaurantPage';
import NotFound from './js/components/NotFound';

class Root extends React.Component {
    render() {
        return (
            <Router>
                <div className="router">
                    <Switch>
                        <Route exact path="/" component={MyFoodApp} />
                        <Route
                            path="/restaurant/:slug"
                            component={RestaurantPage}
                        />
                        <Route render={() => <NotFound />} />
                    </Switch>
                </div>
            </Router>
        );
    }
}

ReactDOM.render(<Root />, document.getElementById('root'));

I found old related posts like this one or this one but there is maybe a new way to handle this issue.

like image 687
Adrien G Avatar asked Dec 23 '22 08:12

Adrien G


2 Answers

There are a couple of solution to your problem without actually using a state management library,

First: Lift the state up,

This is the most correct solution since your components and routes are not highly nested and the data needs to handled by the components which are siblings of each other,

class Root extends React.Component {
    state = {
       restaurant: [] // store the restaurant data here instead of MyFoodApp
    }
    restaurantUpdater = () => {
        //utility function to update state
    }
    render() {
        return (
            <Router>
                <div className="router">
                    <Switch>
                        <Route exact path="/" render={props => <MyFoodApp {...props} restaurant={this.state.data} restaurantUpdater={this.restaurantUpdater} />} />
                        <Route
                            path="/restaurant/:slug"
                            render={props => <RestaurantPage {...props} restaurant={this.state.data} restaurantUpdater={this.restaurantUpdater} />}
                        />
                        <Route render={(props) => <NotFound {...props}/>} />
                    </Switch>
                </div>
            </Router>
        );
    }
}

Second: Make use of Context API

You can make use of Context when your data needs to be passed down to components at different levels. As the react docs say

Don’t use context just to avoid passing props a few levels down. Stick to cases where the same data needs to be accessed in many components at multiple levels.

You can configure the use of context using React 16.3 context API like

export const RestaurantContext = React.createContext({
  restaurant: [],
  restaurantUpdater: () => {},
});

class RestaurantProvider extends React.Component {
   state = {
       restaurant: [] // store the restaurant data here instead of MyFoodApp
    }
    restaurantUpdater = () => {
        //utility function to update state
    }
   render() {
      <RestaurantContext.Provider value={this.state}>
        {this.props.children}
      </RestaurantContext.Provider>
   }
}

and then create an HOC for the consumer

import RestaurantContext from 'path/to/restaurantContext'
const withContext = (Component) => {
    return (props) => <RestaurantContext.Consumer>
        {(restaurant, restaurantUpdater) => <Component {...props} restaurant={restaurant} restaurantUpdater={restaurantUpdater} />}
    </RestaurantContext.Consumer>
}

Now you can use it in the component like

export default withContext(MyFoodApp);

and your FoodApp can use restaurant and restaurantUpdater from props like

const { restaurant, restaurantUpdater} = this.props

similarly you can use it in the other components

like image 59
Shubham Khatri Avatar answered Mar 03 '23 03:03

Shubham Khatri


You could manage data without redux if either of the scenarios matched your arrangement of components:

  1. Parent to child relationship: Using Props
  2. Child to parent relationship: Using callbacks
  3. Siblings with a common parent.

But neither of the relationships satisfies your arrangement of the components since the two components are new root components.

When the components can't communicate between any sort of parent-child relationship, the documentation recommends setting up a global event system.

like image 44
Chasing Unicorn - Anshu Avatar answered Mar 03 '23 03:03

Chasing Unicorn - Anshu