如何在TypeScript的React项目中避免Provider包装嵌套问题?
在TypeScript中实现避免Provider嵌套的方案
我在浏览Twitter时看到George Moller分享了一种避免React Provider嵌套的方案,原JavaScript实现代码如下:
const buildProvidersTree = (componentsWithProps) => { const initialComponent = ({ children }) => <>{children}</>; return componentsWithProps.reduce( (AccumulatedComponents, [Provider, props = {}]) => { return ({ children }) => { return ( <AccumulatedComponents> <Provider {...props}>{children}</Provider> </AccumulatedComponents> ); }; }, initialComponent ); }; const ProvidersTree = buildProvidersTree([ [ThemeContext.Provider], [UserContext.Provider], [QueryClientProvider, { client: queryClient }], [ReduxProvider, { store }], [IntlProvider, { locale: usersLocale }], ]); const root = createRoot(document.getElementById('root')); root.render( <ProvidersTree> <App /> </ProvidersTree> );
TypeScript实现方案
要在TypeScript中实现相同功能,核心是给函数添加准确的类型定义,确保每个Provider的props类型被正确推断,避免类型错误:
import React, { ComponentType, PropsWithChildren } from 'react'; // 定义单个Provider项的类型:[Provider组件, 可选的props(排除children)] type ProviderItem<P = {}> = [ ComponentType<PropsWithChildren<P>>, P? ]; // 构建Providers树的函数,接受Provider数组,返回一个包裹所有Provider的组件 const buildProvidersTree = <T extends ProviderItem[]>( providers: [...T] ): ComponentType<PropsWithChildren<{}>> => { const initialComponent: ComponentType<PropsWithChildren<{}>> = ({ children }) => ( <>{children}</> ); return providers.reduce((AccumulatedComponents, [Provider, props = {} as any]) => { return ({ children }: PropsWithChildren<{}>) => ( <AccumulatedComponents> <Provider {...props}>{children}</Provider> </AccumulatedComponents> ); }, initialComponent); }; // 使用示例 const ProvidersTree = buildProvidersTree([ [ThemeContext.Provider], [UserContext.Provider], [QueryClientProvider, { client: queryClient }], [ReduxProvider, { store }], [IntlProvider, { locale: usersLocale }], ]); const root = createRoot(document.getElementById('root')!); root.render( <ProvidersTree> <App /> </ProvidersTree> );
关键类型说明
ProviderItem<P>:定义每个Provider项的结构,第一个元素是接受children和自定义props的组件,第二个元素是可选的props(自动排除children,因为由我们统一传递)。- 使用泛型
T extends ProviderItem[]让TypeScript自动推断每个Provider的props类型,确保传入的props和Provider组件要求的类型匹配。 initialComponent定义为接受children的空组件,作为reduce的初始值。- 在reduce中,通过
PropsWithChildren<{}>确保返回的组件都能接受children属性。
内容的提问来源于stack exchange,提问作者Juan Carlos Avila Pérez
相关产品推荐
相关产品推荐

