34

新手在这里做出反应并试图将我的头包裹在新的 Context API 上(我还没有研究过 Redux 等)。

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

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

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

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

或者,还有更好的方法?

4

7 回答 7

50

如果您想要一个没有任何第三方库的 Providers 组合解决方案,这里有一个带有 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,当然可以删除注释。

于 2019-11-19T00:11:40.553 回答
3

Solution with for loop:

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;
}

Usage:

<ProviderComposer
    providers={[
        provider(AuthProvider),
        provider(ThemeProvider),
        provider(MuiPickersUtilsProvider, {utils: DateFnsUtils}),
    ]}
>
    <App/>
</ProviderComposer>
于 2020-10-27T16:14:44.647 回答
2

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

感谢@AO17,为ping


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

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

它似乎解决了你的问题。

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

于 2018-07-24T19:29:12.847 回答
1

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

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

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

于 2019-03-20T03:48:35.947 回答
1

几行代码解决了你的问题。

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)
}
于 2019-10-29T00:27:52.133 回答
0

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

于 2021-06-12T20:42:02.700 回答
0

我没有足够的声誉来发表评论,但将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
于 2021-12-03T15:17:45.667 回答