【问题标题】:How to share application State between React Router v4 routes如何在 React Router v4 路由之间共享应用程序状态
【发布时间】:2018-04-20 09:36:04
【问题描述】:

我想知道我需要什么样的解决方案来解决以下问题:

我不使用任何状态管理库。我“只使用”React。

全局应用程序状态存储在组件<MyFoodApp/>中。它包含一个 restaurant 对象数组。

RestaurantPage 组件用于显示餐厅的详细信息并允许创建新评论

此时我被卡住了,因为我需要更新一个特定 restaurant 存储在<MyFoodApp /> 状态的对象,以便添加此提交的评论 .

我曾经通过作为道具传递给子组件的函数来更新状态,但在这种情况下我不能因为MyFoodApp 不是RestaurantPage 的父级。

我必须设置Redux 来解决此问题还是有任何解决方法?

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'));

我找到了旧的相关帖子,例如 this onethis one,但也许有一种新的方法来处理这个问题。

【问题讨论】:

    标签: javascript reactjs react-router-v4


    【解决方案1】:

    在不实际使用状态管理库的情况下,有几个解决方案可以解决您的问题,

    首先:提升状态,

    这是最正确的解决方案,因为您的组件和路由没有高度嵌套,并且数据需要由互为兄弟的组件处理,

    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>
            );
        }
    }
    

    第二:利用Context API

    当您的数据需要向下传递到不同级别的组件时,您可以使用Context。正如反应文档所说的那样

    不要仅仅为了避免将 props 向下传递几个级别而使用上下文。戳 对于需要在许多组件中访问相同数据的情况 在多个层面。

    您可以使用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>
       }
    }
    

    然后为消费者创建一个 HOC

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

    现在你可以在组件中使用它了

    export default withContext(MyFoodApp);
    

    你的 FoodApp 可以使用 restaurantrestaurantUpdater 来自 props 像

    const { restaurant, restaurantUpdater} = this.props
    

    同样你可以在其他组件中使用它

    【讨论】:

    • 您好@shubham-khatri 感谢您的详细回答。我想我会尝试上下文方法。我只是一些问题:也许我什么都不懂,但您似乎没有使用自定义 RestaurantProvider ?我应该用它包装&lt;Router&gt; 组件吗?
    • 你可以封装任意一个Router或者Route组件,
    【解决方案2】:

    如果任一场景与您的组件排列相匹配,您可以在不使用 redux 的情况下管理数据:

    1. 父子关系:使用道具
    2. 子父关系:使用回调
    3. 兄弟姐妹与共同的父母。

    但是这两种关系都不满足您对组件的安排,因为这两个组件都是新的根组件。

    当组件之间无法进行任何形式的父子关系通信时,documentation 建议设置全局事件系统。

    【讨论】:

    • 您好,感谢您的回答。我检查了您提供的链接,但没有找到有关“全局事件系统”的任何文档,您能举个例子吗?
    【解决方案3】:

    您的答案的解决方案是创建子路由以创建从您的 MyFoodApp 组件到 RestaurantPage 组件的子路由。

    MyFoodApp 的渲染方法内 *注意:不要在路由内再次使用 switch <Switch> <Route exact path="/restaurant" render={() => <RestaurantPage pages={this.arrayOfRestaurant}/> </Switch>

    希望对你有帮助

    【讨论】:

    • 您好,感谢您的回复。我更喜欢将我的路线保持在一起。 “提升”状态或使用新的 Context API 似乎符合我的需求。
    猜你喜欢
    • 2022-01-14
    • 2017-04-27
    • 1970-01-01
    • 2016-11-02
    • 2016-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-27
    相关资源
    最近更新 更多