"组件"就是一个简单的JavaScript函数.
"呈现"组件的意思就是"运行函数".
JS函数每次运行时都会重新创建其中的变量,包括函数定义,这一点是正确的.
react 提供的"魔力"避免了看起来像是被重新创造的东西是useState()
、useMemo()
、useCallback()
等等.这些都不能绕过上面提到的基本JS行为.这些钩子在第一次运行和后续运行(即随后的"呈现")时"缓存"值,如果React发现它的缓存中有一个变量/对象/函数,则它将该值返回给由useState/useMemo/useFunction创建的"新"实例,就像在前一次运行中一样.
现在,在上面的示例中,如果handle
属性更改,则它将重新生成子组件,因此将重新呈现它.你至少有两种方法可以避免这种情况:
- 定义
handleIncrement()
时使用useCallback()
(如您所做的……但见下文)
- 将第二个参数传递给
memo()
--该函数用于比较组件以前和当前的props ,如果它们相同则返回true
(即不呈现).因此,只需将第二个参数设置为() => true
,它将永远不会重新生成,也永远不会重新呈现.
现在...你现在的handleIncrement()
你可以使用useCallback()
,它依赖于state
的值. 但是每次调用handleIncrement()
,它都会增加state
的值. 因此,这将导致Parent
重新呈现,并且使用新值state
,useCallback()将重新生成handleIncrement()
作为新函数,因为它的依赖项具有新值.
还有……最后一件事:setState(state + 1);
是一个糟糕的编码模式.因为setState()
是一个异步函数,所以您告诉REACT(好吧,是JavaScript):"在以后的某个时刻,请取当前值state
,将其与1
相加,然后将其放回state
".
这里的问题是,如果在JavaScript开始运行其异步队列之前多次调用setState()
……您可能最终会丢失数据!如果state
的当前值是7
,并且您编码:
setState(state + 1);
setState(state + 100);
setState(state - 2);
然后,当您的组件最终开始重新呈现时,state
的值将是5
……因为JS被告知要运行7 + 1
、7 + 100
和7 - 2
……最后一个获胜.
每当你有一个状态变量,你想更新它的值,使之相对于它的当前值--无论是数组、对象、字符串、整数,无论什么--然后你想使用"设置状态函数"的"回调签名":setState(currVal => currVal + 1)
.
该语法告诉JavaScript"在TIMET上运行设置状态函数,获取其当前值,加1
,然后将其放回状态变量".
使用后一种方法,如果您拨打setState()
setState()
0次,每次都会得到正确的答案.在上述条件相同的情况下,state
等于7
,您可以进行编码:
setState(currVal => currVal + 1);
setState(currVal => currVal + 100);
setState(currVal => currVal - 2);
然后JS会做:7 + 1
、8 + 100
和setState()
- 2
.重新渲染时的值将为setState(state + 1)
.主要区别在于setState(currVal => ....)
获得状态变量109的值,而setState(state + 1)
在setState()
被添加到异步队列时获得值state
*.
如果您还没有,我强烈建议我的所有朋友(并要求我的所有同事)观看视频JavaScript: Understanding the Weird Parts次,特别是关于同步和异步代码执行的部分(视频描述中有时间链接).