【问题标题】:How do I pass State via Context to a Route in React?如何通过上下文将状态传递给 React 中的路由?
【发布时间】:2018-10-22 05:02:24
【问题描述】:

使用 Context API 设置 React 应用程序的最佳方法是什么,这将允许我为应用程序的每个路由提供一些全局状态和方法?

示例:我想在应用程序的顶层处理 REST 和 Auth,所以我希望我的组件在顶层调用请求方法并将响应数据过滤回组件级别。

而且...这种架构在 React 范式中是否有效? (我是 React 新手)

【问题讨论】:

    标签: reactjs react-router react-context


    【解决方案1】:

    你将 Context 中的函数注入到容器 props 中: https://reactjs.org/docs/context.html#accessing-context-in-lifecycle-methods

    如果你愿意,你也可以在没有上下文的情况下这样做,看起来像:

    <Router>
        <Switch>
            <Route path='/' render={props => <Container {...props} API={API} />} />
    

    【讨论】:

    • 很高兴知道这一点。我希望我可以为所有路线做一次。但也许更好的策略是仅在路线的子组件中,在使用它的确切位置获取上下文?
    【解决方案2】:

    TLDR; CodeSandbox Demo

    好的,经过一番努力,我找到了解决问题的方法(我是 React 新手),所以也许这是一个愚蠢的解决方案。如果是这样.. 提供更好的答案,我很乐意接受您的答案。

    解决方案

    使用Unstated library,我可以围绕提供依赖注入的 React Context API 创建一个包装器。这允许我实例化可以传递给组件的实例。

    例如:

    import { Provider, Subscribe } from "unstated";
    import CounterContainer from "./counter";
    
    let counterA = new CounterContainer({ initAmount: 4 });
    let counterB = new CounterContainer({ initAmount: 8 });
    
    const Red = () => {
      return (
        <Subscribe to={[CounterContainer]}>
          {counter => (
            <div className="red">
              <span>{counter.state.count}</span>
              ...
            </div>
          )}
        </Subscribe>
      );
    };
    
    const App = () => (
      <div>
        <Provider inject={[counterA]}>
          <Red />
        </Provider>
        <hr />
        <Provider inject={[counterB]}>
          <Red />
        </Provider>
      </div>
    );
    

    在 CodeSandbox 上试用演示:https://codesandbox.io/s/k3v3wnzrn7

    将 React/Unstated 上下文包装为服务

    我扩展了这个想法,创建了一个框架 API 服务。 API 服务拥有loggedIn 等状态属性,以及login()logout() 等服务方法。这些属性和方法在整个应用程序中都可用,只需在我想使用 API 服务的每个地方进行一次导入。

    我通过将 Unstated Provider、Subscribe 和 Instance 包装在 Api.js 服务中来做到这一点。

    例如:

    Api.js:

    import { Provider, Subscribe, Container } from "unstated";
    
    class APIContainer extends Container {
      constructor(props = {}) {
        super();
    
        this.state = {
          loggedIn: false
        };
      }
    
      async login() {
        console.log("Logging in");
        this.setState({ loggedIn: true });
      }
    
      async logout() {
        console.log("Logging out");
        this.setState({ loggedIn: false });
      }
    }
    
    const Api = {
      Instance: new APIContainer(),
      Provider,
      Subscribe
    };
    
    export default Api;
    

    现在我可以在任何其他文件中包含Api.js 作为提供程序,注入我关心的实例。在这种情况下,只有一个实例,但如果我想运行并行测试,我可以提供不同的实例。

    App.js:

    import Api from "./Api";
    
    class App extends React.Component {
      render() {
        return (
          <div>
            <Api.Provider inject={[Api.Instance]}>
              <Routes />
            </Api.Provider>
          </div>
        );
      }
    }
    
    render(<App />, document.getElementById("root"));
    

    然后我可以在整个应用程序中包含Api.js,并订阅从树中向下传递的任何实例。

    页面/Foo.js:

    import Api from "../Api";
    
    class Foo extends React.Component {
      render() {
        return (
          <Api.Subscribe to={[Api.Instance]}>
            {api => (
              <div>
                <h1>? Foo</h1>
                <pre>
                  api.state.loggedIn = {api.state.loggedIn ? "? true" : "? false"}
                </pre>
                <button onClick={() => api.login()}>Login</button>
                <button onClick={() => api.logout()}>Logout</button>
              </div>
            )}
          </Api.Subscribe>
        );
      }
    }
    
    export default Foo;
    

    在此处尝试 CodeSandbox 演示:https://codesandbox.io/s/wqpr1o6w15

    也许一个更有经验的 React 开发人员可以插话我是否无缘无故地扼杀了 React 框架)...

    【讨论】:

      猜你喜欢
      • 2017-05-03
      • 1970-01-01
      • 1970-01-01
      • 2020-08-16
      • 2016-01-19
      • 2023-03-27
      • 2021-11-26
      • 2021-01-03
      • 1970-01-01
      相关资源
      最近更新 更多