【问题标题】:Too many React Context providers太多的 React 上下文提供程序
【发布时间】:2019-01-01 10:09:48
【问题描述】:

新人在这里做出反应并试图将我的头脑围绕新的 Context API(我还没有研究过 Redux 等)。

似乎我可以做很多我需要做的事情,但我最终会得到很多很多的提供者,所有这些都需要一个标签来包装我的主应用程序。

我将有一个 Auth 提供者,一个用于主题,一个用于聊天消息(访问 Pusher.com)等。另外使用 React Router 是另一个包装器元素。

我将不得不结束这个(以及更多)......

<BrowserRouter>
    <AuthProvider>
        <ThemeProvider>
            <ChatProvider>
                <App />
            </ChatProvider>
        </ThemeProvider>
    </AuthProvider>
</BrowserRouter>

或者有没有更好的方法?

【问题讨论】:

  • 这就是 Redux 解决的问题。
  • 嗯,我担心有人会这么说,但我试图听取那些说在使用 Redux 之前尝试在 React 中学习状态的人的建议。稍微了解一下 Redux 和 MoX,我想我会更有可能尝试 MobX
  • 以上是Redux的一个很好的用例;回击是因为当地状态通常很好。您不想接受不必要的权衡。见this excellent writeup by Redux-author Dan Abramov, “You Might Not Need Redux”
  • 听到很多人说 API Context 或 React 钩子会将 Redux 扔进垃圾箱,但 Redux 仍然是 Redux,所有 3 种方法都应该用于不同的系统。在这种情况下,或者您拥有全局商店,您需要影响所有网站:Redux 是关键,并且将比 API 上下文更强大(通过避免组件占用所有 DOM(例如,用于身份验证或聊天提供者) . 主题可以像 CSS 一样在 Cascading 中更新,因此 API 上下文是更好的选择。
  • 除了列表在视觉上很长导致可见页面变宽之外,这种模式是否真的会产生任何问题?

标签: reactjs react-context


【解决方案1】:

如果您想要一种无需任何第三方库即可编写 Provider 的解决方案,这里有一个带有 Typescript 注释的解决方案:

// Compose.tsx

interface Props {
    components: Array<React.JSXElementConstructor<React.PropsWithChildren<any>>>
    children: React.ReactNode
}

export default function Compose(props: Props) {
    const { components = [], children } = props

    return (
        <>
            {components.reduceRight((acc, Comp) => {
                return <Comp>{acc}</Comp>
            }, children)}
        </>
    )
}

用法:

<Compose components={[BrowserRouter, AuthProvider, ThemeProvider, ChatProvider]}>
    <App />
</Compose>

如果你不使用 Typescript,当然可以删除注释。

【讨论】:

  • 提供者需要时添加属性:const { components = [], children, ...rest } = props. ...... return {acc}
【解决方案2】:

for 循环解决方案:

export const provider = (provider, props = {}) => [provider, props];

export const ProviderComposer = ({providers, children}) => {
    for (let i = providers.length - 1; i >= 0; --i) {
        const [Provider, props] = providers[i];
        children = <Provider {...props}>{children}</Provider>
    }
    return children;
}

用法:

<ProviderComposer
    providers={[
        provider(AuthProvider),
        provider(ThemeProvider),
        provider(MuiPickersUtilsProvider, {utils: DateFnsUtils}),
    ]}
>
    <App/>
</ProviderComposer>

【讨论】:

    【解决方案3】:

    使用@rista404 的答案 - https://stackoverflow.com/a/58924810/4035
    因为react-context-composer 已被弃用。

    感谢@AO17,感谢the ping


    免责声明:我从未使用过这个,只是研究了一下。

    FormidableLabs(他们为许多 OSS projects 做出了贡献)有一个名为 react-context-composer 的项目

    它似乎解决了你的问题。

    React 提出了一个新的 Context API。 API 鼓励编写。 此实用程序组件可帮助您在组件时保持代码干净 将呈现多个上下文提供者和消费者。

    【讨论】:

    • 这看起来很有希望。我不完全理解他们的示例代码(例如 WTH 是 Context?),但我会试一试,如果它有效,我会标记为正确。非常感谢。
    • @jonhobbs 示例使用flow 按照本例,github.com/jamiebuilds/create-react-context#example
    • 包已经存档,使用@rista404的解决方案
    • @AO17 非常感谢您的提醒~更新了答案。
    【解决方案4】:

    几行代码就能解决你的问题。

    import React from "react"
    import _ from "lodash"
    
    /**
     * Provided that a list of providers [P1, P2, P3, P4] is passed as props,
     * it renders
     *
     *    <P1>
            <P2>
              <P3>
                <P4>
                  {children}
                </P4>
              </P3>
            </P2>
          </P1>
     *
     */
    
    export default function ComposeProviders({ Providers, children }) {
      if (_.isEmpty(Providers)) return children
    
      return _.reverse(Providers)
        .reduce((acc, Provider) => {
          return <Provider>{acc}</Provider>
        }, children)
    }
    

    【讨论】:

      【解决方案5】:

      一个简单的解决方案是使用组合函数,如the one Redux uses,将所有提供者组合在一起。然后 compose 函数会这样调用:

      const Providers = compose(
          AuthProvider,
          ThemeProvider,
          ChatProvider
      );
      

      我也没有使用过这个解决方案,但是有了 React 的新钩子功能,你可以使用 react 钩子在函数定义中访问它,而不是渲染你的上下文。

      【讨论】:

      【解决方案6】:

      如果您需要将外部道具注入提供者 elemet 使用 withprops hoc,请重构 js 嵌套助手

      【讨论】:

      • 欢迎来到 StackOverflow!当您回答问题时,请记住提供清晰且有据可查的答案。添加代码片段以解释您的意思和/或提供有价值的链接,以进一步说明您发布的解决方案!
      【解决方案7】:

      我没有足够的声誉来发表评论,但将 rrista404 答案集成在 useCallback() 挂钩中以确保上下文数据在某些情况下(例如页面切换)的完整性可能很有用。

      // Compose.tsx
      
      interface Props {
          components: Array<React.JSXElementConstructor<React.PropsWithChildren<any>>>
          children: React.ReactNode
      }
      
      const Compose = useCallback((props: Props) => {
          const { components = [], children } = props
      
          return (
              <>
                  {components.reduceRight((acc, Comp) => <Comp>{acc}</Comp>, children)}
              </>
          )
      }, [])
      
      export default Compose
      

      【讨论】:

      猜你喜欢
      • 2019-07-24
      • 2020-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-21
      • 2021-01-24
      • 2020-05-20
      • 1970-01-01
      相关资源
      最近更新 更多