react教程,如何卸载一个根组件或子组件?root.unmount()
发布于 作者:苏南大叔 来源:程序如此灵动~首先,react
可不可以主动卸载一个根组件?肯定是可以的,react
必然有这个能力从根源上卸载。那换成卸载子组件呢?那么,也必须是有这个能力的。那么,如何卸载这个根组件或者子组件呢?这就是本文中要探讨的问题。
苏南大叔的程序如此灵动博客,记录苏南大叔和计算机代码的故事。测试环境:create-react-app@5.0.1
,react@18.2.0
,react-dom@18.2.0
,node@16.14.2
。
测试标的组件
苏南大叔的目标是这样的:
1、使用react
的方式卸载组件,而不是通过html
操作dom
的方式来实现这个需求。
2、卸载组件的时候,希望能够把关联的dom
元素也一并卸载。比如利用传送门脱离原有体系的组件。
测试组件:
<>
<div className='class' ref={this.nodeRef}>带class的组件</div>
{this.state.show.a1 && <p>普通组件a1</p>}
{this.state.show.a2 && (<div onClick={this.handleClick}>
传送门组件外部a2
<Modal>
<button>传送门组件内部</button>
</Modal>
</div>)}
</>
有关传送门组件的定义:
https://newsn.net/say/react-portal.html
方案一,.parentNode.removeChild()
首先需要肯定的是:react
最终也是在页面上形成了传统的元素节点。所以,使用.removeChild
的方式,是必然可以删除节点的。但是,这操作超出了react
的概念。附加在被删除节点上的ref
或者state
之类的数据,可能会造成一些问题。
let _node = document.querySelector('.class');
_node.parentNode.removeChild(_node);
或者
let _node = this.nodeRef.current;
_node.parentNode.removeChild(_node);
原理上就是使用真实节点的父节点来删除对应真实节点。所以,它必然可以生效,但是也必然必能关联删除附加节点(比如传送门生成的节点)。
方案二,root.unmount()
/root.render("")
这个方案仅仅针对根组件root
生效,是无法控制子组件的。而root
一般是通过props.root
从最外层传递进来的变量,例如:
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<App root={root} />
);
这样的话,在<App/>
内部,就可以通过props.root
来进行整体组件卸载了。两种方式:
props.root.unmount();
或者
props.root.render("");
ReactDOM.unmountComponentAtNode(真实dom)
,在最新的react@18
系列中已经不存在这个函数了。
方案三,.createRoot().unmount()
/.createRoot().render("")
这个方案可以处理子组件,但是不能处理根组件root
。原理上来说,上一个方案中的root
是已经被.createRoot()
过的。已经被处理过的.createRoot()
不能再次被处理,这也是root
不能使用本方案的原因。
这个方案原理就是把子组件的真实dom
,变成一个react
的根组件,然后再unmount()
或者render("")
,达到卸载子组件的目的。现在假设真实dom
是个className
为aaa
的子组件。
ReactDOM.createRoot(document.querySelector('.aaa')).unmount();
或者
ReactDOM.createRoot(document.querySelector('.aaa')).render("");
虽然这个方案能够达到目的,但是苏南大叔总是感觉哪里不对劲。这个函数的结果并不像是对原来的组件进行卸载,而且对原结构进行了破坏...
方案四,state && <组件/>
【推荐】
苏南大叔认为:react
和传统的页面逻辑的区别就在于:行动的主动权在谁手里。不同于传统页面,react
更像是一种自发式的组织形式,并没有谁能够主动掌控组件的生死。所以从react
的方式来思考问题的话,主动卸载某个组件的函数是不是理论上不存在啊...
那么,以react
的思路来解释这个需求的话。苏南大叔认为:完美方案就是state && <组件/>
。当不想要这个组件的时候,就切换一下state
即可。对应组件就会被卸载,而不是隐藏。
this.state = { show: { a1: true, a2: true } }
{this.state.show.a1 && <p>a1</p>}
{this.state.show.a2 && (<div>
<modal>content</modal>
</div>)}
let _next_show = { ...this.state.show, ...{ a1: false } };
this.setState({ show: _next_show });
测试代码
以下是本文的完整测试代码:
import React, { createRef } from 'react';
import ReactDOM from 'react-dom/client';
import ReactDOMPortal from 'react-dom';
const root2 = document.getElementById('root-modal');
class Modal extends React.Component {
constructor(props) {
super(props);
this.el = document.createElement('div');
}
componentDidMount() {
root2.appendChild(this.el);
}
componentWillUnmount() {
root2.removeChild(this.el);
}
render() {
return ReactDOMPortal.createPortal(
this.props.children,
this.el
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.root = props.root;
this.handleClick = this.handleClick.bind(this);
this.nodeRef = createRef(null);
this.state = { show: { a1: true, a2: true } }
}
handleClick() {
// document.querySelector('.class').parentNode.removeChild(document.querySelector('.class'));
// this.nodeRef.current.parentNode.removeChild(this.nodeRef.current);
// this.root.unmount();
// this.root.render("");
// ReactDOM.unmountComponentAtNode(this.nodeRef.current);
// ReactDOM.createRoot(document.querySelector('.class')).unmount();
// ReactDOM.createRoot(document.querySelector('.class')).render("");
let _next_show = { ...this.state.show, ...{ a2: false } };
this.setState({ show: _next_show });
}
render() {
return (
<>
<div className='class' ref={this.nodeRef}>带class的组件</div>
{this.state.show.a1 && <p>普通组件a1</p>}
{this.state.show.a2 && (<div onClick={this.handleClick}>
传送门组件外部a2
<Modal>
<button>传送门组件内部</button>
</Modal>
</div>)}
</>
);
}
}
export default App;
结束语
从react
的角度来想这件事情的话,本文中的这个需求的大部分内容可能是个伪需求。因为主动让某个东西做某事,这并不符合react
的思路,更优的思路是:根据某个状态或情形,组件自己主动做某事。驱动力来自自身,而非外界强迫。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。