We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
usePersistFn可以持久化function,保证函数地址永远不会变化。
import { useRef } from 'react'; export type noop = (...args: any[]) => any; function usePersistFn<T extends noop>(fn: T) { const fnRef = useRef<T>(fn); // 每次渲染fn的最新值都会记录在fnRef中 fnRef.current = fn; const persistFn = useRef<T>(); // 初次渲染时给persistFn赋值,此后persistFn不会更新 if (!persistFn.current) { persistFn.current = function (...args) { return fnRef.current!.apply(this, args); } as T; } // 返回persistFn,感叹号表示返回值类型非null或undefined,因为初次渲染时persistFn就被赋值为了函数 return persistFn.current!; } export default usePersistFn;
在React官方文档中提到
React
在某些场景中,你可能会需要用 useCallback 记住一个回调,但由于内部函数必须经常重新创建,记忆效果不是很好,导致子组件重复 render。对于超级复杂的子组件,重新渲染会对性能造成影响。通过 usePersistFn,可以保证函数地址永远不会变化。
官方给出的demo如下
function Form() { const [text, updateText] = useState(''); const textRef = useRef(); useEffect(() => { textRef.current = text; // 把它写入 ref }); const handleSubmit = useCallback(() => { const currentText = textRef.current; // 从 ref 读取它 alert(currentText); }, [textRef]); // 不要像 [text] 那样重新创建 handleSubmit return ( <> <input value={text} onChange={e => updateText(e.target.value)} /> <ExpensiveTree onSubmit={handleSubmit} /> </> ); }
ExpensiveTree是一个复杂的子组件,其接受一个props handleSubmit函数。如果使用useCallback,由于handleSubmit函数内部使用了text变量,便要写为如下形式:
ExpensiveTree
props
useCallback
const handleSubmit = useCallback(() => { alert(text); }, [text]);
只要text发生变化,useCallback接收的内部函数便要重新创建,导致handleSubmit函数的引用地址发生变化。进而引起子组件ExpensiveTree的重渲染,对性能产生影响。
usePersistFn的目标便是持久化接收的函数,且调用时内部函数引用的变量(上例为text)能获取到实时的值(useCallback的依赖传空数组也能实现持久化函数,但无法获取实时的值)
usePersistFn
useEffect
参见这个issue
如果在子组件的useEffect回调函数中调用usePersistFn就会出现问题。因为渲染时会先执行子组件的useEffect,后执行父组件自定义hooks的useEffect。
demo参见在useEffect中更新fnRef
The text was updated successfully, but these errors were encountered:
请问persistFn定义和赋值为什么要分两行,直接:
const persistFn = useRef<T>(function (...args) { return fnRef.current!.apply(this, args); } as T)
有区别吗? 还有,这里不用箭头函数,也不一样吗?
Sorry, something went wrong.
请问persistFn定义和赋值为什么要分两行,直接: const persistFn = useRef<T>(function (...args) { return fnRef.current!.apply(this, args); } as T) 有区别吗? 还有,这里不用箭头函数,也不一样吗?
不好意思现在才看到。定义赋值可以不分开写的,没有区别,这里我是贴的ahooks源码😂。用箭头函数会更改函数调用时的this指向,这里只能用function。
ahooks
No branches or pull requests
usePersistFn
usePersistFn可以持久化function,保证函数地址永远不会变化。
为什么要用usePersistFn?
在
React
官方文档中提到官方给出的demo如下
ExpensiveTree
是一个复杂的子组件,其接受一个props
handleSubmit函数。如果使用useCallback
,由于handleSubmit函数内部使用了text变量,便要写为如下形式:只要text发生变化,
useCallback
接收的内部函数便要重新创建,导致handleSubmit函数的引用地址发生变化。进而引起子组件ExpensiveTree
的重渲染,对性能产生影响。usePersistFn
的目标便是持久化接收的函数,且调用时内部函数引用的变量(上例为text)能获取到实时的值(useCallback
的依赖传空数组也能实现持久化函数,但无法获取实时的值)官方给的demo中更新textRef写在了
useEffect
中,为什么usePersistFn
不这样实现?参见这个issue
如果在子组件的
useEffect
回调函数中调用usePersistFn
就会出现问题。因为渲染时会先执行子组件的useEffect
,后执行父组件自定义hooks的useEffect
。demo参见在useEffect中更新fnRef
The text was updated successfully, but these errors were encountered: