You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 07:23:22