react 动态组件

需求:

  在某个 位置 动态的插入和删除自定义组件,比如点击切换页面内容

实现思想:

  1. 利用 React.createElement(), 创建或者引用自己定义的组件
  2. 利用 React.render() 真实渲染插入 自己的组件 dom
  3. 利用 React.unmountComponentAtNode(),销毁某个dom节点下的 react 组件节点

核心代码:

/**
   * 动态插入组件
   * @param com  com 可以是html 标签字符串,也可以是react class组件 或者是 函数组件
   */
  function loadComponent(com) {
    //获取要插入位置的节点
    let container = document.getElementById('container你的dom id');
    // 卸载之前组件
    if(container) ReactDOM.unmountComponentAtNode(container)
    ReactDOM.render(React.createElement(com), container);
  }

​参考demo:

1.定义的一些组件 

AnalyzeMobxDevtools.tsx

function AnalyzeMobxDevtools(){
    return (
        <div>AnalyzeMobxDevtoolsAnalyzeMobxDevtoolsAnalyzeMobxDevtoolsAnalyzeMobxDevtoolsAnalyzeMobxDevtools</div>
    )
}

export default AnalyzeMobxDevtools;

SyncUIStateObservable.tsx

function SyncUIStateObservable(){
    return (
        <div>SyncUIStateObservableSyncUIStateObservableSyncUIStateObservableSyncUIStateObservableSyncUIStateObservable</div>
    )
}

export default SyncUIStateObservable;

2.归纳所有的组件到一个函数里,便于应用

代码结构

AllComponents.ts

import SyncUIStateObservable from './mobx/SyncUIStateObservable'
import AnalyzeMobxDevtools from './mobx/AnalyzeMobxDevtools'

/**
 * 所有的组件 ,用于页面切换 
 */
export default {
    "mobx": {
        2: SyncUIStateObservable,
        1: AnalyzeMobxDevtools
    },
    "jsdesignpar":{
        0:1
    }

}

3.页面插入

app.tsx

import logo from './logo.svg';
import './App.css';
import { Layout, Menu, Breadcrumb } from 'antd';
import { UserOutlined, LaptopOutlined, NotificationOutlined } from '@ant-design/icons';
import ReactDOM from 'react-dom'
import React from 'react'
import AllComponents from './AllComponents'
const { SubMenu } = Menu;
const { Header, Content, Sider } = Layout;
function App() {

  /**
   * 动态插入组件
   * @param com 
   */
  function loadComponent(e: any) {
    let container = document.getElementById('container');

    // 卸载之前组件
    if(container) ReactDOM.unmountComponentAtNode(container)
    switch (e.item.props.datatype) {
      case 'mobx':
        const coms = AllComponents as any 
        const typeComs = coms[e.item.props.datatype]
        ReactDOM.render(React.createElement(typeComs[e.key]), container);
        break;
      default:
        break;
    }
    
  }

  return (
    <Layout style={{ height: "100%" }}>
      <Header className="header">
        <div className="logo" />
        <Menu theme="dark" mode="horizontal" defaultSelectedKeys={['2']}>
          <Menu.Item key="1">nav 1</Menu.Item>
          <Menu.Item key="2">nav 2</Menu.Item>
          <Menu.Item key="3">nav 3</Menu.Item>
        </Menu>
      </Header>

      <Layout >
        <Sider width={200} className="site-layout-background">
          <Menu
            mode="inline"
            defaultSelectedKeys={['1']}
            defaultOpenKeys={['sub1']}
            style={{ height: '100%', borderRight: 0 }}
          >
            <SubMenu key="sub1" icon={<UserOutlined />} title="subnav 1">
              <Menu.Item key="1" datatype="mobx" onClick={loadComponent}>option1</Menu.Item>
              <Menu.Item key="2" datatype="mobx" onClick={loadComponent}>option2</Menu.Item>
              <Menu.Item key="3" datatype="mobx" onClick={loadComponent}>option3</Menu.Item>
              <Menu.Item key="4" datatype="mobx" >option4</Menu.Item>
            </SubMenu>
            <SubMenu key="sub2" icon={<LaptopOutlined />} title="subnav 2">
              <Menu.Item key="5">option5</Menu.Item>
              <Menu.Item key="6">option6</Menu.Item>
              <Menu.Item key="7">option7</Menu.Item>
              <Menu.Item key="8">option8</Menu.Item>
            </SubMenu>
            <SubMenu key="sub3" icon={<NotificationOutlined />} title="subnav 3">
              <Menu.Item key="9">option9</Menu.Item>
              <Menu.Item key="10">option10</Menu.Item>
              <Menu.Item key="11">option11</Menu.Item>
              <Menu.Item key="12">option12</Menu.Item>
            </SubMenu>
          </Menu>
        </Sider>

        <Layout style={{ padding: '0 24px 24px' }}>

          <Breadcrumb style={{ margin: '16px 0' }}>
            <Breadcrumb.Item>Home</Breadcrumb.Item>
            <Breadcrumb.Item>List</Breadcrumb.Item>
            <Breadcrumb.Item>App</Breadcrumb.Item>
          </Breadcrumb>

          <Content
            className="site-layout-background"
            id="container"
            style={{
              padding: 24,
              margin: 0,
              minHeight: 280,
            }}
          >
            Content
        </Content>

        </Layout>

      </Layout>

    </Layout>
  );
}

export default App;

4.效果