2022-11-29 qiankun call root.render() on the existing root instead if you want to update it.

使用阿里qiankun微前端解决方案构建测试案例时遇到一个问题,那就是主应用在卸载子应用时,浏览器console报了如下的错误:

[
react:18.2.0
]

Warning: You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before. Instead, call root.render() on the existing root instead if you want to update it.

咋一看是Warning,无需理会,但是细看,这问题很严重啊,也就是子应用出生时挂载的dom和卸载时的dom不是同一个啊,原因就是子应用暴露生命周期的方法对象用错了

import "./public_path";
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

//将渲染过程封装,以便使用
function render(props) {
  const { container } = props;
  const root = container ? ReactDOM.createRoot(container.querySelector('#root')) : ReactDOM.createRoot(document.querySelector('#root'));
  root.render(
        <React.StrictMode>
            <App></App>
        </React.StrictMode>);
}

/**如果不是被基座加载,则正常渲染*/
if (!window .__POWERED_BY_QIANKUN__) {
    render({});
}


//bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
//通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
export async function bootstrap() {
    console.log('react app bootstraped');
}
//应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法,以及监听基座传递的消息
export async function mount(props) {
    console.log('props from main framework', props);
    render(props);
    props.onGlobalStateChange((state, prev) => {
        console.log('子应用监听',state,prev);
    });
}

export async function unmount(props) {
    const { container } = props;
    const root = container? createRoot(container.querySelector("#root")): createRoot(document.querySelector("#root"));
    root.uninstall();
}

export async function update(props) {
    console.log('update props', props);
}

最终版本(将root容器抽取下):

import "./public_path";
import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App";

/**
 *  挂载和卸载需要保持root是同一个对象,不然浏览器会报警告信息
 *  
 */
let root = undefined;

function getRoot(container) {
  if (root) {
    return root;
  }
  root = container
    ? createRoot(container.querySelector("#root"))
    : createRoot(document.querySelector("#root"));
  return root;
}

//将渲染过程封装,以便使用
function render(props) {
  const { container } = props;
  getRoot(container).render(
    <React.StrictMode>
      <App></App>
    </React.StrictMode>
  );
}

//如果不是被基座加载,则正常渲染
if (!window.__POWERED_BY_QIANKUN__) {
  render({});
}

//bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
//通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。

export async function bootstrap() {
  console.log("react app bootstraped");
}
//应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法,以及监听基座传递的消息
export async function mount(props) {
  console.log("props from main framework", props);
  render(props);
  props.onGlobalStateChange((state, prev) => {
    console.log("子应用监听", state, prev);
  });
}

export async function unmount(props) {
  const { container } = props;
  getRoot(container).unmount();
  console.log("micro-one uninstall..");
  /** 这个地方切记要重置为未定义 */
  root = undefined;
}

export async function update(props) {
  console.log("update props", props);
}

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容