在渲染功能之外访问React Context


93

我正在使用新的React Context API而不是Redux开发一个新应用,并且在使用之前,例如Redux,当我需要获取用户列表时,我只是调用componentDidMount我的操作,但是现在使用React Context,我的操作可以在内部进行。我的使用者位于我的渲染函数中,这意味着每次调用我的渲染函数时,它将调用我的操作以获取用户列表,这不好,因为我将执行许多不必要的请求。

那么,如何只能一次调用动作,例如incomponentDidMount而不是in render?

只是为了举例说明,请看以下代码:

假设我将所有内容包装Providers在一个组件中,如下所示:

import React from 'react';

import UserProvider from './UserProvider';
import PostProvider from './PostProvider';

export default class Provider extends React.Component {
  render(){
    return(
      <UserProvider>
        <PostProvider>
          {this.props.children}
        </PostProvider>
      </UserProvider>
    )
  }
}

然后,我将这个Provider组件包装了所有应用程序,如下所示:

import React from 'react';
import Provider from './providers/Provider';
import { Router } from './Router';

export default class App extends React.Component {
  render() {
    const Component = Router();
    return(
      <Provider>
        <Component />
      </Provider>
    )
  }
}

现在,以我的用户视图为例,它将是这样的:

import React from 'react';
import UserContext from '../contexts/UserContext';

export default class Users extends React.Component {
  render(){
    return(
      <UserContext.Consumer>
        {({getUsers, users}) => {
          getUsers();
          return(
            <h1>Users</h1>
            <ul>
              {users.map(user) => (
                <li>{user.name}</li>
              )}
            </ul>
          )
        }}
      </UserContext.Consumer>
    )
  }
}

我想要的是:

import React from 'react';
import UserContext from '../contexts/UserContext';

export default class Users extends React.Component {
  componentDidMount(){
    this.props.getUsers();
  }

  render(){
    return(
      <UserContext.Consumer>
        {({users}) => {
          getUsers();
          return(
            <h1>Users</h1>
            <ul>
              {users.map(user) => (
                <li>{user.name}</li>
              )}
            </ul>
          )
        }}
      </UserContext.Consumer>
    )
  }
}

但是,当然上面的示例不起作用,因为getUsers不存在于我的用户视图中的道具。如果有可能的话,正确的做法是什么?

Answers:


143

编辑:随着v16.8.0中对react-hooks的引入,您可以通过使用useContexthook在功能组件中使用上下文

const Users = () => {
    const contextValue = useContext(UserContext);
    // rest logic here
}

编辑:从版本16.6.0起。您可以通过使用上下文的生命周期的方法this.context一样

class Users extends React.Component {
  componentDidMount() {
    let value = this.context;
    /* perform a side-effect at mount using the value of UserContext */
  }
  componentDidUpdate() {
    let value = this.context;
    /* ... */
  }
  componentWillUnmount() {
    let value = this.context;
    /* ... */
  }
  render() {
    let value = this.context;
    /* render something based on the value of UserContext */
  }
}
Users.contextType = UserContext; // This part is important to access context values

在版本16.6.0之前,您可以按以下方式进行操作

为了在您的生命周期方法中使用Context,您可以将组件编写为

class Users extends React.Component {
  componentDidMount(){
    this.props.getUsers();
  }

  render(){
    const { users } = this.props;
    return(

            <h1>Users</h1>
            <ul>
              {users.map(user) => (
                <li>{user.name}</li>
              )}
            </ul>
    )
  }
}
export default props => ( <UserContext.Consumer>
        {({users, getUsers}) => {
           return <Users {...props} users={users} getUsers={getUsers} />
        }}
      </UserContext.Consumer>
)

通常,您将在您的应用程序中维护一个上下文,将上述登录信息打包到HOC中以便重新使用是有意义的。你可以这样写

import UserContext from 'path/to/UserContext';

const withUserContext = Component => {
  return props => {
    return (
      <UserContext.Consumer>
        {({users, getUsers}) => {
          return <Component {...props} users={users} getUsers={getUsers} />;
        }}
      </UserContext.Consumer>
    );
  };
};

然后你可以像这样使用它

export default withUserContext(User);

这是一种方法!但是我用其他东西包装了我的组件,因此这种方式的代码将变得越来越复杂。因此,使用with-context库及其装饰器可使代码更加简单。但是,谢谢您的回答!
GustavoMendonça18年

它对我不起作用,我使用with-context github
PassionateDeveloper18年

1
Shubham Khatri的回答是正确的,只有一个小错字阻止了它的运行。花括号阻止了隐式返回。只需将它们替换为括号即可。
VDubs 18/09/26

1
您能否解释一下如何this.context在同一个组件中的多个上下文中使用?假设我有一个需要访问UserContext和PostContext的组件,该如何处理呢?我看到的是创建一个将两者混合的上下文,但这是唯一或更好的解决方案?
GustavoMendonça19年

1
@GustavoMendonça您只能使用this.context API实现订阅单个上下文。如果您需要阅读多个内容,则需要遵循渲染道具模式
Shubham Khatri 19'Jan

3

好的,我找到了一种有限制的方法。通过该with-context库,我设法将我的所有消费者数据插入到我的组件道具中。

但是,要在同一个组件中插入多个使用方很复杂,您必须使用此库创建混合使用方,这会使代码不够美观且效率低下。

该库的链接:https : //github.com/SunHuawei/with-context

编辑:实际上,您不需要使用提供的多上下文api,with-context实际上,您可以使用简单的api并为每个上下文创建一个装饰器,如果您想在组件中使用多个消费者,则只需在您的课程上方声明所需的装饰数!


1

就我而言,添加.bind(this)到事件中就足够了。这就是我的组件的外观。

// Stores File
class RootStore {
   //...States, etc
}
const myRootContext = React.createContext(new RootStore())
export default myRootContext;


// In Component
class MyComp extends Component {
  static contextType = myRootContext;

  doSomething() {
   console.log()
  }

  render() {
    return <button onClick={this.doSomething.bind(this)}></button>
  }
}

1
该代码看起来简单得多,但实际上根本不访问值或与RootStore实例进行交互。您能否显示一个拆分为两个文件和响应式UI更新的示例?
dcsan

-7

您必须在较高级的父级组件中传递上下文,以在子级中作为道具获得访问权限。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.