【问题标题】:How to type custom `preload` method for React.lazy components?如何为 React.lazy 组件键入自定义的“预加载”方法?
【发布时间】:2022-01-15 00:28:45
【问题描述】:

当尝试为React.lazy() 组件实现preload() 方法时,典型的模式类似于,

const ReactLazyPreload = (importStatement) => {
  const Component = React.lazy(importStatement);
  Component.preload = importStatement; // Property 'preload' does not exist on type 'LazyExoticComponent<T>'.
  return Component;
};

以后可以使用,例如,

const MyComponent = ReactLazyPreload(() => import("./MyComponent.tsx");
const onHover = () => { MyComponent.preload() };

但是第一个sn-p第3行的赋值导致TS错误,

Property 'preload' does not exist on type 'LazyExoticComponent&lt;T&gt;'.

我一直在玩declare,但未能成功消除错误。 preload() 方法应该使用什么类型?

【问题讨论】:

    标签: reactjs typescript react-suspense


    【解决方案1】:

    TypeScript 试图防止你在这里犯错。

    仅仅因为其他人遵循约定并不能使它成为一个好的约定:在这种情况下,它不是一个安全的约定。 一般来说,对不属于你的东西进行变异是不安全的。

    虽然我在当前版本标记 (17.0.2) 的 React 代码库中找不到任何似乎会导致将某些内容分配给惰性组件的 preload 属性的问题,但这并不意味着React 维护者不会在后续版本中使用此属性。如果发生这种情况,并且您覆盖了该属性,则会出现不可预知的行为。

    不要改变组件,只需在其旁边返回预加载函数:

    TS Playground link

    import {default as React, lazy} from 'react';
    import type {ComponentType, LazyExoticComponent} from 'react';
    
    export type ReactLazyFactory<T = any> = () => Promise<{default: ComponentType<T>}>;
    
    export type ComponentPreloadTuple<T = any> = [
      component: LazyExoticComponent<ComponentType<T>>,
      preloadFn: () => void,
    ];
    
    export function getLazyComponentWithPreload <T = any>(componentPath: string): ComponentPreloadTuple<T>;
    export function getLazyComponentWithPreload <T = any>(factory: ReactLazyFactory<T>): ComponentPreloadTuple<T>;
    export function getLazyComponentWithPreload <T = any>(input: string | ReactLazyFactory<T>): ComponentPreloadTuple<T> {
      const factory = () => typeof input === 'string' ? import(input) : input();
      return [lazy(factory), factory];
    }
    
    
    // ----------
    // Example.tsx
    
    export type ExampleProps = {
      text: string;
    };
    
    export default function ExampleComponent ({text}: ExampleProps) {
      return <div>{text}</div>;
    }
    
    
    // ----------
    // AnotherComponent.tsx
    
    // use with path to component:
    const [Example1, preloadExample1] = getLazyComponentWithPreload<ExampleProps>('./Example');
    
    // use with factory function:
    const [Example2, preloadExample2] = getLazyComponentWithPreload<ExampleProps>(() => import('./Example'));
    
    

    【讨论】:

    • 提供一个泛型中包含 any 的答案让我很痛苦,但它是当前 React 类型声明文件的正确类型?
    【解决方案2】:
    // extend lazy component with `preload` property
    interface LazyPreload<Props>
      extends React.LazyExoticComponent<React.ComponentType<Props>> {
      preload: () => {};
    }
    
    function ReactLazyPreload<Props>(
      importStatement: () => Promise<{ default: React.ComponentType<Props> }>
    ) {
      // use Object.assign to set preload
      // otherwise it will complain that Component doesn't have preload
      const Component: LazyPreload<Props> = Object.assign(
        React.lazy(importStatement),
        {
          preload: importStatement,
        }
      );
    
      return Component;
    }
    

    【讨论】:

      猜你喜欢
      • 2020-06-06
      • 1970-01-01
      • 1970-01-01
      • 2013-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多