这篇文章主要讲解了“useEvent降低Hooks负担的原生Hook分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“useEvent降低Hooks负担的原生Hook分析”吧!
没有 useEvent 的时候
我们先看看不用 useEvent 的情况:
function Chat() { const [text, setText] = useState(''); // ???? Always a different function const onClick = () => { sendMessage(text); }; return <SendButton onClick={onClick} />;}
其中点击事件的回调函数 onClick
中需要读取当前键入的文本text
,这里的onClick
随着组件重新渲染一次次地重新创建,每次都会是不一样的引用,这显然带来了性能损耗,如果你想对其进行优化,你可能会这样做:
function Chat() { const [text, setText] = useState(''); // ???? A different function whenever `text` changes const onClick = useCallback(() => { sendMessage(text); }, [text]); return <SendButton onClick={onClick} />;}
通过 useCallback 返回一个 memoized 回调函数。
useCallback: 返回一个 memoized 回调函数。 把内联回调函数及依赖项数组作为参数传入 useCallback
,它将返回该回调函数的 memoized
版本,该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate
)的子组件时,它将非常有用。 useCallback(fn, deps)
相当于 useMemo(() => fn, deps)
。
最终使得onClick
的引用始终不变但是!
onClcik
这个方法有需要保证每次都要拿到最新的、正确的text,所以他的deps
中就自然是设置了text
—— 坏了,“又回到最初的起点~”。随着每一次keystroke
,onClick
又变成了上面的情况:
Always a different function
但你又不能将其从deps
中移除,移除了他就只能拿到text
的初始值,失去了他本该有的功能...
小 useEvent 来给他整个活
useEvent
就是为了解决此类问题,所以他干脆不要deps
了,他就是一直返回一个相同的函数引用,哪怕text
发生变化。当然,保证它也能拿到最新的、正确的**text**
。
function Chat() { const [text, setText] = useState(''); // ✅ Always the same function (even if `text` changes) const onClick = useEvent(() => { sendMessage(text); }); return <SendButton onClick={onClick} />;}
现在好了:
onClick 的引用始终是同一个
保证每次都能拿到最新的、正确的
text
当然还有其他一些场景,但是大致需求原理相同,就是不想让A
因为b
变化而总是重新加载,但是又因为要拿到b
恰当的值,所以deps
中必须b
,导致不得不重新加载,掉进了“圈圈圆圆圈圈~”的陷阱。更多场景这里就不再赘述。更多案例可查看文末的学习资源~
总而言之,用useEvent
给他裹上就是香,就是可以同时达到上面两个效果:
引用不变
拿到恰当的值
这是咋做到的????
说了这么多,我们来看看他这是咋做到的
他大概是这么个形状:(不是源码就长这样的意思嗷)
// (!) Approximate behaviorfunction useEvent(handler) { const handlerRef = useRef(null); // In a real implementation, this would run before layout effects useLayoutEffect(() => { handlerRef.current = handler; }); return useCallback((...args) => { // In a real implementation, this would throw if called during render const fn = handlerRef.current; return fn(...args); }, []);}
先回顾几个Hook相关知识点:
useRef
useRef:
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内持续存在。
这里通过 useRef 保存回调函数handler
到handlerRef.current
,然后再在 useCallback 中从handlerRef.current
来取函数再调用,这样避免了直接调用,跳出了闭包陷阱。并且不出意外的话handler
在整个生命周期内持续存在,也就是只有一个引用。
useLayoutEffect
这个 useLayoutEffect 可能没那么常用,我们来看看这是啥嘞
其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。
useEffect
回顾一下 useEffect
默认情况下,effect 将在每轮渲染结束后执行
两者的区别
好了,现在我给你用一个字总结一下两者区别,useLayoutEffect
更“快”!这个“块”不是速度更快,而是他“抢跑”了哩。useLayoutEffect
是在render
之前同步执行,useEffect
在render
之后异步执行,这里就是保证useLayoutEffect
里的回调肯定比useEffect
更早前被调用、被执行。
useCallback执行时机
前面说到
useCallback(fn, deps)
相当于 useMemo(() => fn, deps)
。
文档里是这样说 useMemo 的:
记住,传入 useMemo 的函数会在渲染期间执行。请不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的适用范畴,而不是 useMemo。
也就是他是在render
时执行的,也就是保证了赋值handler
给handlerRef.current
是在前面发生
这里的作用
这里返回的是一个useCallback
包裹后 memoized
函数,其中从handlerRef.current
中获取函数,并且deps
为[]
,也就是说他不会再次更新。
感谢各位的阅读,以上就是“useEvent降低Hooks负担的原生Hook分析”的内容了,经过本文的学习后,相信大家对useEvent降低Hooks负担的原生Hook分析这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!