React hooks 已于 6 年前发布,但直到今天,我们仍然可以看到错误的发生,即使是高级 React 工程师也是如此。在最新版本的 React 文档中,核心团队付出了很大的努力来教授 useEffect 的错误用例,但在实际项目中错误仍然不断发生。
在这篇文章中,让我们尝试一种不同的方法。让我们了解 React 与派生的关系以及为什么应该更多地使用它。
React 并不是完全响应式的,但最终,对于创建应用程序的开发人员来说,这并不重要。这里的区别在于,React 遵循的粗粒度方法创建了重新渲染的必要性,以真正识别状态转换创建的更改。
所以,考虑一个像这样的简单 React 组件:
function Example() { const [count, setCount] = useState(0) const text = `count is ${count}`; return ( <button onClick={() => setCount((count) => count + 1)}> {text} </button> ); }
当我们更改计数状态时,调用 setCount,我们会更新状态值并安排重新渲染。好吧,React 将重新渲染这个组件,再次调用它。此时,如果我问 React 渲染发生了什么变化,答案应该是什么?
可能是一个谦虚的“我不知道”。
React 不会像其他细粒度反应式库那样使用复杂的数据结构来管理其依赖项来跟踪其状态。 React 需要再次调用该组件。在这个新调用中,创建的计数常量将具有新值,在其上方我们将使用字符串创建一个新常量,例如 “count is 1”,如果计数值更改为 1。
然后 JSX 将通过一次更改生成其结构,按钮内部文本不是 “计数为 1”,React 会进行协调过程,识别此更改并将其应用到真实 DOM。显而易见,对吧?
此时,如果我问 React 发生了什么变化,它可能会回答:“示例组件中的按钮文本”。
但是等等,文本常量呢?它也发生了变化,为什么它创建的 v-dom (我知道这个术语的问题,但它通常是这样称呼的)结构很重要?
对于 React,你内部创建的变量和常量并不重要。重要的是状态改变,然后是组件调用的返回。
中间的所有内容都是创建视图结构过程的一部分。当然,所有这些数据都会影响返回的 JSX,这就是 React 模型的要点,关心组件调用的结果并根据视图相应地更新 DOM。
您可能会看到 React 模型的简化:
function Example() { const [count, setCount] = useState(0) const text = `count is ${count}`; return ( <button onClick={() => setCount((count) => count + 1)}> {text} </button> ); }
查看状态函数的结果。在这种情况下,视图是根据状态变化的派生。因此,对于这个术语和内部组件数据,React 是一个推导机。
您在组件内创建的每个变量和常量在该组件调用期间都将处于活动状态。在我们上面使用的示例中,示例组件的每次重新渲染都会创建一个新的常量文本。如果我需要基于某些道具或状态的新值,它只会在计算中使用这些状态和道具创建一个新变量。
让我们以 React 文档为例:
view= f(state)
我们这里遇到一些问题。首先,国家的本质。
为什么我们在应用程序中需要这样的本地状态?保留数据并允许用户更改它。 fullName 状态不是由用户更改,而是由 useEffect 更改。它正在利用其他状态来创造新的价值。同样,在 React 中,我们在组件内部创建的每个变量和常量都可以使用 states 和 props 来计算其值:派生,React 的默认行为.
这个例子还有另一个问题,关于运行时。在这种情况下,在第一次渲染中,fullName 值将为空字符串。 React 将获取该组件的 JSX 返回,将其渲染到 UI,遵循浏览器绘制过程,然后调用组件的 useEffects。此时我们将进行 setfullName 调用,它将安排新的重新渲染。 React 将再次调用该组件,现在完整名称为 Taylor Swift,然后使用新的文本值更新 UI。
就运行时而言,您正在进行 2 次渲染,其中一次是不必要的,且数据错误。它在性能和稳定性方面更差,因为用户会看到该值为空,并且可以被视为视觉错误。
所以,按照React的推导模型,我们可以简单地修改为:
function Form() { const [firstName, setFirstName] = useState('Taylor'); const [lastName, setLastName] = useState('Swift'); // ? Avoid: redundant state and unnecessary Effect const [fullName, setFullName] = useState(''); useEffect(() => { setFullName(firstName + ' ' + lastName); }, [firstName, lastName]); //... return <span>{fullName}</span>; }
现在我们只有 1 个渲染,避免了不必要的渲染。我们将避免仅使用推导来使用效果。每次重新渲染时都会使用最新版本的状态值进行更新。
对,在本例中只需使用 useMemo,添加与 useEffect 上使用的相同的依赖项数组。 React 的记忆模型只是避免默认行为的一种方法,即在每次重新渲染时创建一个新的派生。使用 useMemo,您可以手动跟踪状态和 props,如果其中一些状态和 props 发生变化,只需再次创建派生。
useEffect 对于值更改的外部同步是必要的。在 UI 开发中,很少有这种情况有意义,因为通常外部更改(例如服务器 API 调用)发生在用户操作上(顺便说一下,我们正在创建用户界面),所以这些将发生在事件处理程序上,而不是在 useEffect 中。
如果您使用 useEffect 来更新状态,也许您可以使用派生进行相同的更新,从而避免之前提到的所有问题。
如果推导不起作用,或者你遇到了少数特定情况之一,或者国家的设计或解决方案本身有问题。没有问题,但在这些情况下,最好检查组件并避免组件代码将来出现问题。
这是 React 中推导的基础知识,但我们这里缺少一些东西。
当我需要进行异步推导(例如简单的 GET 请求)时会发生什么,该请求使用某些状态作为参数并且每次状态更改时都需要重新计算?
这是下一篇文章的主题。
再见!
以上是你不知道 React 的推导的详细内容。更多信息请关注PHP中文网其他相关文章!