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