【问题标题】:Context API consume from anywhere上下文 API 从任何地方消费
【发布时间】:2018-09-03 16:27:36
【问题描述】:

我正在尝试使用 React 上下文 API 在我的应用程序中实现共享状态。

我正在树的根部创建一个errorContext 状态。错误上下文如下所示:

// ErrorContext.js
import React from 'react';
const ErrorContext = React.createContext({
    isError: false,
    setError:   (error) => {}
});

export default ErrorContext;

期望的结果

  • 我想从应用中的任何位置(特别是在 Promise 中)更新(使用)此上下文
  • 理想情况下,消耗步骤应提取到导出的辅助函数中

帮助函数的示例用法

http.get('/blah')
    .catch((error) => {
        HelperLibrary.setError(true);
    })

按照反应上下文文档:

我可以像这样创建一个提供者:

class ProviderClass {
    state = {
        isError: false,
        setError: (error) => {
            this.state.isError = error;
        }
    }

    render() {
        return (
            <ErrorContext.Provider value={this.state}>
                {this.props.children}
            </ErrorContext.Provider>
        )
    }
}

然后我可以通过在渲染调用中使用 Consumer 包装器来使用此提供程序:

<ErrorContext.Consumer>
    {(context) => {
        context.setError(true); 
    }}
</ErrorContext.Consumer>

这种方法的问题

这种方法需要我团队中的每个开发人员在每次希望处理 Web 服务错误时编写大量样板代码。

例如他们必须将 ErrorContext.Consumer 放在组件 render() 方法中,并根据 Web 服务响应有条件地呈现它。

我尝试过的

在辅助函数中使用 ReactDOM.render。

const setError = (error) =>{
       ReactDOM.render(
           <ErrorContext.Consumer>
               // boilerplate that i mentioned above
           </ErrorContext.Consumer>,

           document.getElementById('contextNodeInDOM')
       )    }
       export default setError;

为什么这不起作用?

出于某种原因,ReactDOM.render() 总是将此代码放在 React 组件树之外。

<App>
    ...
        <ProviderClass>
            ...
            <div id="contextNodeInDOM'></div> <-- even though my node is here
            ...
        </ProviderClass>
</App>
<ErrorContext.Consumer></ErrorContext.Consumer> <-- ReactDOM.render puts the content here

因此没有为消费者找到上下文父级,因此它默认为默认上下文(没有状态)

来自docs

如果上面这个上下文没有提供者,则 value 参数 将等于传递给 createContext() 的 defaultValue。

如果有人可以在我的下一步中帮助我,我来自 Angular,如果我的术语不正确或者我做了一些非常愚蠢的事情,我深表歉意。

【问题讨论】:

  • 上下文 API 的目的是不允许从任何地方使用它,而只能在 组件内部使用,否则它不会与任何应用程序范围的变量不同。在 Angular 术语中, 执行 DI。您可以跳过样板代码,类似于答案的建议。

标签: reactjs


【解决方案1】:

您可以导出一个 HOC 以在导出之前包装错误组件,消除样板并确保仅在需要时提供上下文,并且不会弄乱 DOM:

// error_context.js(x)
export const withErrorContext = (Component) => {
  return (props) => (
    <ErrorContext.Consumer>
      {context => <Component {...props} errorContext={context} />}
    </ErrorContext.Consumer>
  )
};

// some_component.js(x)
const SomeComponent = ({ errorContext, ...props }) => {
  http.get('/blah')
      .catch((error) => {
        errorContext.setError(true);
      })

  return(
    <div></div>
  )
};

export default withErrorContext(SomeComponent);

现在 React 16.8 已经登陆,您还可以使用 hooks 更干净地做到这一点:

const SomeComponent = props => {
  const { setError } = useContext(ErrorContext)

  http.get("/blah").catch(() => setError(true))

  return <div />
}

【讨论】:

  • 我喜欢这种方法,我会告诉你我的进展情况。
【解决方案2】:

按照反应上下文文档:

我可以像这样创建一个提供者:

class ProviderClass { state = { isError: false, setError: (error) => { this.state.isError = error; } }

我不这么认为 - 应该使用 setState 反应中有一个一般规则“不要改变状态 - 使用 setState() " - 滥用会导致大部分反应问题。

我感觉你不了解上下文角色/用法。这更像是一种通往全局存储的快捷方式,无需通过深层组件结构(有时更多超过 10 个级别)。

App > CtxProvider > Router > Other > .. > CtxConsumer > ComponentConsumingCtxStorePropsNMethods

在某些特殊情况下使用 id 访问渲染的 DOM 节点,一般应避免,因为后续渲染会破坏外部所做的任何更改。

如果您需要在主 react 应用程序 html 节点之外的某处呈现某事,请使用 portals

【讨论】:

  • 您好,感谢您的回答。当您说您认为不应该存在 setError 时,我会要求您阅读 ContextAPI 上的 React 文档。另外我认为您误读了我的问题,我不想在主反应应用程序之外呈现,这就是问题所在。不幸的是 ReactDOM.render 将节点放在树之外。
  • setError 的正文中不应使用this.state.sth =。那么你不应该使用ReactDOM.render - 在任何你需要的地方使用HOC。
  • 您还可以在许多地方使用此上下文,一个集中用于显示消息,并在其余地方访问错误方法。这将类似于 redux 的使用。
猜你喜欢
  • 2012-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多