多个 React 上下文:内部一个依赖于外部一个不起作用

Multiple React Context: inner one depending on outer one doesn't work

我想为我的应用程序使用 2 个 React 上下文。

  1. 第一个上下文是UserContext,加载用户信息。
  2. 第二个上下文是 ItemContext,它根据用户 ID 加载项目信息。

为简单起见,我使用同步函数来加载用户和项目信息。有关完整的工作示例,请参阅 CodeSandbox

// index.js

import React from "react";
import ReactDOM from "react-dom";
import { ItemProvider, ItemConsumer } from "./ItemContext";
import { UserProvider, UserConsumer } from "./UserContext";

class App extends React.Component {
  render() {
    return (
      <UserProvider>
        <UserConsumer>
          {({ user }) => (
            <ItemProvider user={user}>
              <ItemConsumer>
                {({ item }) => <div>{JSON.stringify(item, null, 2)}</div>}
              </ItemConsumer>
            </ItemProvider>
          )}
        </UserConsumer>
      </UserProvider>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

// UserContext.js
import React from "react";

const { Provider, Consumer } = React.createContext();

class UserProvider extends React.Component {
  state = {
    isLoading: false,
    value: { user: {} }
  };

  async componentDidMount() {
    this.setState({ isLoading: true });
    const user = { id: 1, name: "evan" };
    this.setState({ value: { user }, isLoading: false });
  }

  render() {
    const { isLoading, value } = this.state;
    if (isLoading) {
      return <div>Loading data...</div>;
    }
    return <Provider value={value}>{this.props.children}</Provider>;
  }
}

export { UserProvider, Consumer as UserConsumer };

// ItemContext.js
import React from "react";

const { Provider, Consumer } = React.createContext();

const items = { 1: { name: "a red hat" }, 2: { name: "a blue shirt" } };

const getItemByUserId = userId => items[userId];

class ItemProvider extends React.Component {
  state = {
    isLoading: false,
    value: { item: {} }
  };

  async componentDidMount() {
    this.setState({ isLoading: true });
    const item = getItemByUserId(1);
    // console.log(this.props.user); // Object {}
    // const item = getItemByUserId(this.props.user.id);
    this.setState({ value: { item }, isLoading: false });
  }

  render() {
    const { isLoading, value } = this.state;
    if (isLoading) {
      return <div>Loading data...</div>;
    }
    return <Provider value={value}>{this.props.children}</Provider>;
  }
}

export { ItemProvider, Consumer as ItemConsumer };

我无法在 ItemContext 中执行 const item = getItemByUserId(this.props.user.id); 因为 this.props.user 是一个空对象。

解决方法是什么?

用户值从 UserContext 中异步呈现,并且在子项呈现之前不可用,因此您需要在 ItemProvider 中实现 componentDidUpdate 以根据用户属性获取上下文值

import React from "react";

const { Provider, Consumer } = React.createContext();

const items = { 1: { name: "a red hat" }, 2: { name: "a blue shirt" } };

const getItemByUserId = userId => items[userId];

class ItemProvider extends React.Component {
  state = {
    isLoading: false,
    value: { item: {} }
  };

  async componentDidMount() {
    if (this.props.user && this.props.user.id) {
      this.setState({ isLoading: true });
      const item = getItemByUserId(this.props.user.id);
      this.setState({ value: { item }, isLoading: true });
    }
  }

  componentDidUpdate(prevProps) {
    console.log(this.props.user, prevProps.user);
    if (prevProps.user !== this.props.user) {
      const item = getItemByUserId(this.props.user.id);
      this.setState({
        value: { item }
      });
    }
  }

  render() {
    const { isLoading, value } = this.state;
    if (isLoading) {
      return <div>Loading data...</div>;
    }
    console.log(value);
    return <Provider value={value}>{this.props.children}</Provider>;
  }
}

export { ItemProvider, Consumer as ItemConsumer };

Working demo