1、return null组件不会
在render中调用return null, 组件不会调用componentWillUnmount,不会销毁组件实例,只是不会渲染。所以,如果该组件存在一个状态比如useState创建的show等状态,会在下次渲染的时候保留。 举个例子,我们通常有两种方式让组件不渲染到页面上。如下:
// 1、外部
{show ? <Bottom /> : null}
// 2、内部
const Bottom = (show: boolean) => {
const [color, setColor] = useState('black');
//...
if (show){
return null
}
return (
<div>test</div>
)
}
一种是外部条件判断,如果show为false,那组件就不存在了,会被销毁。第二种是组件return null。组件实例不会被销毁,当再次渲染的时候,color会保留上次的值。
2、状态与渲染树中的位置相关
当向一个组件添加状态时,那么可能会认为状态“存在”在组件内。但实际上,状态是由 React 保存的。React 通过组件在渲染树中的位置将它保存的每个状态与正确的组件关联起来。 React 会使相同位置的相同组件的 state 被保留下来,其他情况比如不同位置的相同组件或者相同位置的不同组件会使state重置。详见# 对 state 进行保留和重置。 这里还解释了为什么不在组件内定义组件的原因。
import { useState } from 'react';
export default function MyComponent() {
const [counter, setCounter] = useState(0);
function MyTextField() {
const [text, setText] = useState('');
return (
<input
value={text}
onChange={e => setText(e.target.value)}
/>
);
}
return (
<>
<MyTextField />
<button onClick={() => {
setCounter(counter + 1)
}}>点击了 {counter} 次</button>
</>
);
}
每次你点击按钮,输入框的 state 都会消失!这是因为每次 MyComponent 渲染时都会创建一个 不同 的 MyTextField 函数。你在相同位置渲染的是 不同 的组件,所以 React 将其下所有的 state 都重置了。这样会导致 bug 以及性能问题。为了避免这个问题, 永远要将组件定义在最上层并且不要把它们的定义嵌套起来。
3、react渲染树
上面提到了react渲染树,简单介绍一下。React使用虚拟DOM来管理渲染树,并且通过比较前后两个虚拟DOM树的差异来进行高效的更新。当组件被销毁时,React会在下一次渲染时检测到该组件已经不存在于新的虚拟DOM树中,然后将其从页面中移除。
这种方式使得React能够高效地处理组件的销毁,而无需重新布局整个渲染树。只有需要更新的部分会被重新渲染,并且只有相关的生命周期方法(如componentWillUnmount)会被调用。
需要注意的是,React会在组件销毁时执行一些清理工作,但不会立即释放组件所占用的内存。内存的释放是由JavaScript的垃圾回收机制负责的,它会在适当的时机自动回收不再使用的内存。
上面提到的state在相同位置相同组件会被保留。实际上就是DOM树在相同位置进行前后比较,diff算法比较的结果是同一个组件,所以保留了当前组件,没有被移除,所以对应状态自然就保留下来了。