自定义hooks
作为react的使用者,自定义hooks是常见的业务需求,小弟技术水平有限,只能先借花献佛聊下公司里大佬们封装的自定义hooks了
import { useMemo, useRef } from 'react';
type noop = (...args: any[]) => any;
/**
* 持久化 function 的 Hook
* @param fn 需要持久化的函数
* @returns 引用地址永远不会变化的 fn
*/
export function useMemoizedFn<T extends noop>(fn: T) {
if (process.env.NODE_ENV !== 'development') {
if (typeof fn !== 'function') {
console.error(`useMemoizedFn expected parameter is a function, got ${typeof fn}`);
}
}
const fnRef = useRef<T>(fn);
fnRef.current = useMemo(() => fn, [fn]);
const memoizedFn = useRef<T>();
if (!memoizedFn.current) {
memoizedFn.current = function(...args) {
// eslint-disable-next-line @typescript-eslint/no-invalid-this
return fnRef.current.apply(this, args);
} as T
}
return memoizedFn.current;
}
useMemoizedFn 会保留每次运行过程中最新的函数地址引用,通常用于较为复杂的组件的props传递中以减少不必要的re-render。
用法如下
import React, { useState, useCallback } from 'react';
import { Button, Space, message } from 'antd';
import { useMemoizedFn } from '@pansy/react-hooks';
export default () => {
const [count, setCount] = useState(0);
const callbackFn = useCallback(
() => {
message.info(`Current count is ${count}`);
},
[count]
);
const memoizedFn = useMemoizedFn(() => {
message.info(`Current count is ${count}`);
})
return (
<>
<p>count: {count}</p>
<Space>
<Button
onClick={() => {
setCount((c) => c + 1);
}}
>
Add Count
</Button>
<Button
onClick={callbackFn}
>
call callbackFn
</Button>
<Button
onClick={memoizedFn}
>
call memoizedFn
</Button>
</Space>
</>
)
}
import React, { useState, useCallback, useRef } from 'react';
import { Button, Space, message } from 'antd';
import { useMemoizedFn } from '@pansy/react-hooks';
export default () => {
const [count, setCount] = useState(0);
const callbackFn = useCallback(() => {
message.info(`Current count is ${count}`);
}, [count]);
const memoizedFn = useMemoizedFn(() => {
message.info(`Current count is ${count}`);
});
return (
<>
<p>count: {count}</p>
<Button
onClick={() => {
setCount((c) => c + 1);
}}
>
Add Count
</Button>
<p>You can click the button to see the number of sub-component renderings</p>
<div style={{ marginTop: 32 }}>
<h3>Component with useCallback function:</h3>
{/* use callback function, ExpensiveTree component will re-render on state change */}
<ExpensiveTree showCount={callbackFn} />
</div>
<div style={{ marginTop: 32 }}>
<h3>Component with useMemoizedFn function:</h3>
{/* use memoized function, ExpensiveTree component will only render once */}
<ExpensiveTree showCount={memoizedFn} />
</div>
</>
);
}
const ExpensiveTree = React.memo<{ [key: string]: any }>(({ showCount }) => {
const renderCountRef = useRef(0);
renderCountRef.current += 1;
return (
<div>
<p>Render Count: {renderCountRef.current}</p>
<Button onClick={showCount}>
showParentCount
</Button>
</div>
);
});
个人的分享记录,不喜勿喷。