如何使用多版本的React

在开发过程中使用联邦模块功能,使用其他项目的React组件,会出现 Minified React error #321; 错误。这是由于多个版本的React导致的。
在React@17中,事件系统进行了更改。这可以让我们使用React的多版本,解决多个版本的React导致的错误。

导出组件出口

我们创建一个函数,来包裹我们需要导出的组件出口。
import type { FunctionComponent, ComponentClass } from 'react';
import { render, unmountComponentAtNode } from 'react-dom';

interface CreateLegacyRoot {
  render(props: any): void;
  unmount(): void;
}

type LegacyReturn = (container: HTMLElement) => CreateLegacyRoot;

/**
 * 创建一个createLegacyRoot函数,保证组件能够兼容多个react版本
 * @param { FunctionComponent | ComponentClass } Component: 组件
 */
function legacy(Component: FunctionComponent | ComponentClass): LegacyReturn {
  return function createLegacyRoot(container: HTMLElement): CreateLegacyRoot {
    return {
      render(props: any): void {
        render(<Component { ...props } />, container);
      },
      unmount(): void {
        unmountComponentAtNode(container);
      }
    };
  };
}

export default legacy;
然后导出一个方法,供其他位置调用。
export const createLegacyRoot = legacy(Component);

在其他项目调用方法

我们创建一个Loader函数,来加载导出的方法。
import { useRef, useEffect } from 'react';
import PropTypes from 'prop-types';

/**
 * 加载联邦模块
 * @param { Function } props.loader: 加载的联邦模块
 * @param { object } props.moduleProps
 */
function Loader(props) {
  const { loader, moduleProps } = props;
  const containerRef = useRef(), // dom,ReactDOM.render渲染的节点
    rootRef = useRef(); // 保存root对象

  // 加载并渲染模块
  async function loadModule() {
    const { createLegacyRoot } = await loader();

    rootRef.current = createLegacyRoot(containerRef.current);
    rootRef.current.render(moduleProps);
  }

  useEffect(function() {
    loadModule();

    return function() {
      rootRef.current.unmount();
    };
  }, [loader, moduleProps]);

  return <div ref={ containerRef } />;
}

Loader.propTypes = {
  loader: PropTypes.func,
  moduleProps: PropTypes.object
};

export default Loader;
然后加载组件并调用。
function App(props) {
  function handleClick(event) {
    alert('你点击了按钮。');
  }

  return (
    <div>
      <Loader loader={ () => import('project/module') }
        moduleProps={{ onClick: handleClick }}
      />
    </div>
  );
}