如何通过上下文将状态传递给 React 中的路由?

How do I pass State via Context to a Route in React?

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

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

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

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

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

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

TLDR; CodeSandbox Demo

好的,经过一些工作,我找到了解决问题的方法(我是 React 的新手),所以这可能是一个愚蠢的解决方案。如果是这样..提供更好的答案,我很乐意接受你的答案。

解决方案

使用 Unstated library,我可以围绕提供依赖注入的 React 上下文 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 服务的地方导入即可。

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

例如:

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,并订阅从树向下传递的任何实例。

Pages/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 Framework)...