太多的 React 上下文提供者
Too many React Context providers
新手在这里做出反应并试图围绕新的 Context API(我还没有研究 Redux 等)。
似乎我可以做很多我需要做的事情,但我最终会遇到很多很多供应商,所有供应商都需要一个标签来包装我的主要应用程序。
我将为 Auth 提供一个提供程序,一个用于主题化,一个用于聊天消息(vis Pusher.com)等。另外使用 React Router 是另一个包装器元素。
我是否必须以这个(以及更多)结束......
<BrowserRouter>
<AuthProvider>
<ThemeProvider>
<ChatProvider>
<App />
</ChatProvider>
</ThemeProvider>
</AuthProvider>
</BrowserRouter>
或者有更好的方法吗?
使用@rista404 的回答 -
因为 react-context-composer
已弃用。
感谢@AO17,the ping。
免责声明:我从未使用过这个,只是研究了一下。
FormidableLabs(他们为许多 OSS projects) has a project called, react-context-composer
这似乎解决了您的问题。
React is proposing a new Context API. The API encourages composing.
This utility component helps keep your code clean when your component
will be rendering multiple Context Providers and Consumers.
一个简单的解决方案是使用组合函数,如the one Redux uses,将所有提供程序组合在一起。然后组合函数将像这样调用:
const Providers = compose(
AuthProvider,
ThemeProvider,
ChatProvider
);
我还没有使用过这个解决方案,但是使用 React 的新钩子功能,而不是渲染你的上下文,你可以使用反应钩子在函数定义中访问它。
几行代码解决你的问题。
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)
}
如果您想要一种无需任何第三方库即可编写 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,你当然可以删除注释。
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>
如果您需要将外部道具注入提供者元素,请重构 js 嵌套助手,使用 withprops hoc
我没有足够的声誉来发表评论,但集成 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
新手在这里做出反应并试图围绕新的 Context API(我还没有研究 Redux 等)。
似乎我可以做很多我需要做的事情,但我最终会遇到很多很多供应商,所有供应商都需要一个标签来包装我的主要应用程序。
我将为 Auth 提供一个提供程序,一个用于主题化,一个用于聊天消息(vis Pusher.com)等。另外使用 React Router 是另一个包装器元素。
我是否必须以这个(以及更多)结束......
<BrowserRouter>
<AuthProvider>
<ThemeProvider>
<ChatProvider>
<App />
</ChatProvider>
</ThemeProvider>
</AuthProvider>
</BrowserRouter>
或者有更好的方法吗?
使用@rista404 的回答 -
因为 react-context-composer
已弃用。
感谢@AO17,the ping。
免责声明:我从未使用过这个,只是研究了一下。
FormidableLabs(他们为许多 OSS projects) has a project called, react-context-composer
这似乎解决了您的问题。
React is proposing a new Context API. The API encourages composing. This utility component helps keep your code clean when your component will be rendering multiple Context Providers and Consumers.
一个简单的解决方案是使用组合函数,如the one Redux uses,将所有提供程序组合在一起。然后组合函数将像这样调用:
const Providers = compose(
AuthProvider,
ThemeProvider,
ChatProvider
);
我还没有使用过这个解决方案,但是使用 React 的新钩子功能,而不是渲染你的上下文,你可以使用反应钩子在函数定义中访问它。
几行代码解决你的问题。
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)
}
如果您想要一种无需任何第三方库即可编写 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,你当然可以删除注释。
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>
如果您需要将外部道具注入提供者元素,请重构 js 嵌套助手,使用 withprops hoc
我没有足够的声誉来发表评论,但集成 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