2

我试图通过实现 AsyncCompoment 类来延迟加载 React 中的路由,如此处所述Code Splitting in Create React App。下面是教程中的 es6 asyncComponent 函数:

import React, { Component } from "react";

    export default function asyncComponent(importComponent) {
      class AsyncComponent extends Component {
        constructor(props) {
          super(props);

          this.state = {
            component: null
          };
        }

        async componentDidMount() {
          const { default: component } = await importComponent();

          this.setState({
            component: component
          });
        }

        render() {
          const C = this.state.component;

          return C ? <C {...this.props} /> : null;
        }
      }

      return AsyncComponent;
    }

我已经在 typescript 中编写了这个函数,并且可以确认组件确实是延迟加载的。我面临的问题是它们没有被渲染。我能够确定组件对象在 componentDidMount 挂钩中始终未定义:

//AsyncComponent.tsx
async componentDidMount() {
              const { default: component } = await importComponent();

              this.setState({
                component: component
              });
            }

从 importComponent 函数返回的对象具有以下属性:

{
MyComponent: class MyComponent: f,
__esModule: true
}

我修改了 componentDidMount 方法以获取该对象的第一个属性,即 MyComponent 类。在此更改之后,我的项目现在延迟加载组件并正确渲染它们。

async componentDidMount() {
          const component = await importComponent();

          this.setState({
            component: component[Object.keys(component)[0]]
          });
        }

我最好的猜测是我没有在打字稿中正确写下这一行:

const { default: component } = await importComponent();

我这样调用 asyncComponent 方法:

const MyComponent = asyncComponent(()=>import(./components/MyComponent));

有人知道如何在打字稿中实现 AsyncComponent 吗?我不确定简单地获取 esModule 对象上的 0 索引是否是正确的方法。

4

1 回答 1

7
// AsyncComponent.tsx
import * as React from "react";

interface AsyncComponentState {
  Component: null | JSX.Element;
};
interface IAsyncComponent {
  (importComponent: () => Promise<{ default: React.ComponentType<any> }>): React.ComponentClass;
}

const asyncComponent: IAsyncComponent = (importComponent) => {
  class AsyncFunc extends React.PureComponent<any, AsyncComponentState> {
    mounted: boolean = false;
    constructor(props: any) {
      super(props);

      this.state = {
        Component: null
      };
    }
    componentWillUnmount() {
      this.mounted = false;
    }
    async componentDidMount() {
      this.mounted = true;
      const { default: Component } = await importComponent();
      if (this.mounted) {
        this.setState({
          component: <Component {...this.props} />
        });
      }

    }


    render() {
      const Component = this.state.Component;
      return Component ? Component : <div>....Loading</div>
    }
  }
  return AsyncFunc;

}
export default asyncComponent;


// Counter.tsx

import * as React from 'react';
import { RouteComponentProps } from 'react-router';

interface CounterState {
  currentCount: number;
}

class Counter extends React.Component<RouteComponentProps<{}>, CounterState> {
  constructor() {
    super();
    this.state = { currentCount: 0 };
  }

  public render() {
    return <div>
      <h1>Counter</h1>

      <p>This is a simple example of a React component.</p>

      <p>Current count: <strong>{this.state.currentCount}</strong></p>

      <button onClick={() => { this.incrementCounter() }}>Increment</button>
    </div>;
  }

  incrementCounter() {
    this.setState({
      currentCount: this.state.currentCount + 1
    });
  }
}
export default Counter;

//routes.tsx
import * as React from 'react';
import { Route } from 'react-router-dom';
import { Layout } from './components/Layout';
import { Home } from './components/Home';
import asyncComponent from './components/AsyncComponent';


const AsyncCounter = asyncComponent(() => import('./components/Counter'));

export const routes = <Layout>
  <Route exact path='/' component={Home} />
  <Route path='/counter' component={AsyncCounter} />
</Layout>;
于 2017-11-01T16:46:31.310 回答