在带有 hooks 的 React 中,生命周期不存在
很久很久以前,我们使用过带有类的 React,还记得吗?
那时,我们有生命周期方法的概念,即类中的方法接受在特定时刻执行的回调。主要有三种:挂载时、更新时和卸载时。
古老但黄金级别的课程
这很重要,在类组件上,返回的 JSX 是在 render 方法上生成的,状态附加到组件的 this 上,应用程序开发人员需要一种方法来知道在特定时刻执行的操作。我们对组件的生命周期有这样的想法:
当然,您有一个重要的 API,例如“forceUpdate”,如果您使用与 React 状态更新无关的外部数据,它允许您手动触发**重新渲染**。
从概念层面上讲,我们有一种更直接的方式来引导应用程序的流程。生命周期方法遵循与 DOM 元素类似的生命周期,您可以自己执行“memo”和“forceUpdates”,同步状态是执行逻辑的默认方式。
这种直接性被认为是简单的,与反应式模型相比,学习这些概念更容易。但是后来,钩子出现了,改变了一切。
未命名的反应性
这种转变令人困惑。首先,为了简化并保持开发人员对 React 模型的概念愿景,许多交流都试图展示钩子模型的相似之处。为了拥有 3 个主要生命周期方法,他们展示了使用“useEffect”的解决方法。
// componentDidMount useEffect(() => { // code... // componentWillUnmount: return function cleanup() { // code... }; }, []); // componentDidUpdate useEffect(() => { // code... }, [dependencyState, dependencyProp]);
因此,大多数使用 hooks 编写的新 React 代码都遵循了这种思路,开始同步状态是一个自然的过程。为了保持生命周期方法的相同思路,它是调用 setState 并触发重新渲染过程的地方。
同步状态成为一个问题,错误使用“useEffect”成为一个问题,双重重新渲染成为一个问题,过多的重新渲染成为一个问题,性能成为一个问题。
至少对我来说,React 的这一步有点令人困惑。因为转向 hooks 就是转向响应式模型,即使它是一个粗粒度的模型。但沟通表明并没有发生什么重大变化。没有关于响应式概念和理论的内容,即使我已经使用 React 多年,我也只是在阅读 Ryan Carniato 关于响应式和 Solidity 的博客文章后才开始真正理解响应式。
即使知道 `useEffect` 存在误用,我也确实不明白为什么,而且缺乏关于反应性的概念理论使得使用 hooks 时犯错误变得如此容易。`useEffect` 成为了最令人讨厌的 hook,有些人称之为 **“useFootgun”**。关键是,React 中存在概念上的混淆,表现为我们今天看到的 `useEffect` 的所有问题。
**useEffect 问题不是问题的原因,而是结果。**
那么带钩子的生命周期呢
事情是这样的,在反应性的概念中,没有生命周期。
你有一个变化,你对它做出反应,产生副作用。效果是结果,而不是原因。没有状态同步,也没有挂载和卸载的概念。
在卸载之前,它是第一次、第十次还是最后一次渲染都无关紧要,顺便说一下,钩子不会关心它,即使是“useEffect”也是如此。
尝试一下:
function EffectExample() { const [count, setCount] = useState(0); useEffect(() => { console.log('effect', count); return () => { console.log('clean up', count); } }, [count]); return ( ) }
您将在“控制台”上看到每次状态更新时都会执行这两个函数。首先是清理函数,然后是效果回调。如果您使用“useEffect”和某些状态或属性进行订阅,则每次依赖项发生变化时,都会调用清理函数,然后调用新的回调,再次执行订阅,但使用新值。
你应该将你的应用程序代码视为简化的 React 模型:
UI = fn(state)
如果您有如下组件:
function Example() { const [count, setCount] = useState(0); return ( ) }
当您单击按钮并将计数增加 +1 时,**从概念上**,您真正拥有的是这样的:
// first render = fn({ count: 0 }); // button click - re-render = fn({ count: 1 }); // button click - re-render = fn({ count: 2 }); // button click - re-render = fn({ count: 3 });
每次点击都会再次调用 fn,并采用新状态,从而生成新版本的 UI。状态应根据用户的操作或应使用异步派生生成的异步值而改变。
这样你就保持了干净的想法:
一个干净且一致的模型。
渲染器需要关注在屏幕上添加、更新和删除元素。在组件级别,重要的是:
Hooks 及其响应式模型使 React 与浏览器解耦,让应用代码不再关心您处于屏幕渲染过程的哪个时刻。您不再需要强制更新,甚至不再需要按照自己的规则处理备忘录,这对应用开发人员来说不那么直接,但在模型方面却更直接。
**每次重新渲染都会生成一个结构,React 会处理剩下的部分。**