Repository: alibaba/hooks
Pull request #987 (2021-06-17 06:54:23)

Refactor: useMemoizedFn


Patch:
diff --git a/packages/hooks/src/index.ts b/packages/hooks/src/index.ts
index 29631512..1df1bdbf 100644
--- a/packages/hooks/src/index.ts
+++ b/packages/hooks/src/index.ts
@@ -31,7 +31,7 @@ import useUnmount from './useUnmount';
 import useAntdTable from './useAntdTable';
 import useFusionTable from './useFusionTable';
 import useSet from './useSet';
-import usePersistFn from './usePersistFn';
+import useMemoizedFn from './useMemoizedFn';
 import useMap from './useMap';
 import useCreation from './useCreation';
 import useDrop from './useDrop';
@@ -97,7 +97,7 @@ export {
   useAntdTable,
   useUnmount,
   useSet,
-  usePersistFn,
+  useMemoizedFn,
   useMap,
   useCreation,
   useDrag,
diff --git a/packages/hooks/src/useAntdTable/index.ts b/packages/hooks/src/useAntdTable/index.ts
index bde02ce5..52bfb7c9 100644
--- a/packages/hooks/src/useAntdTable/index.ts
+++ b/packages/hooks/src/useAntdTable/index.ts
@@ -9,7 +9,7 @@ import {
   PaginatedResult,
 } from '@ahooksjs/use-request/lib/types';
 import useUpdateEffect from '../useUpdateEffect';
-import usePersistFn from '../usePersistFn';
+import useMemoizedFn from '../useMemoizedFn';
 
 export {
   CombineService,
@@ -226,7 +226,7 @@ function useAntdTable<R = any, Item = any, U extends Item = any>(
     _submit();
   }, [form, _submit]);
 
-  const resetPersistFn = usePersistFn(reset);
+  const resetPersistFn = useMemoizedFn(reset);
 
   // refreshDeps 变化，reset。
   useUpdateEffect(() => {
@@ -235,7 +235,7 @@ function useAntdTable<R = any, Item = any, U extends Item = any>(
     }
   }, [...refreshDeps]);
 
-  const submit = usePersistFn((e) => {
+  const submit = useMemoizedFn((e) => {
     if (e && e.preventDefault) {
       e.preventDefault();
     }
diff --git a/packages/hooks/src/useCookieState/index.ts b/packages/hooks/src/useCookieState/index.ts
index fbaaa1d5..81ff1a89 100644
--- a/packages/hooks/src/useCookieState/index.ts
+++ b/packages/hooks/src/useCookieState/index.ts
@@ -19,24 +19,22 @@ function useCookieState(cookieKey: string, options: IOptions = {}) {
     return options.defaultValue;
   });
 
-  // usePersistFn 保证返回的 updateState 不会变化
+  // useMemoizedFn 保证返回的 updateState 不会变化
   const updateState = useCallback(
     (
       newValue?: TCookieState | ((prevState: TCookieState) => TCookieState),
       newOptions: Cookies.CookieAttributes = {},
     ) => {
       const { defaultValue, ...restOptions } = { ...options, ...newOptions };
-      setState(
-        (prevState: TCookieState): TCookieState => {
-          const value = isFunction(newValue) ? newValue(prevState) : newValue;
-          if (value === undefined || value === null) {
-            Cookies.remove(cookieKey);
-          } else {
-            Cookies.set(cookieKey, value, restOptions);
-          }
-          return value;
-        },
-      );
+      setState((prevState: TCookieState): TCookieState => {
+        const value = isFunction(newValue) ? newValue(prevState) : newValue;
+        if (value === undefined || value === null) {
+          Cookies.remove(cookieKey);
+        } else {
+          Cookies.set(cookieKey, value, restOptions);
+        }
+        return value;
+      });
     },
     [cookieKey, options],
   );
diff --git a/packages/hooks/src/useCountDown/index.ts b/packages/hooks/src/useCountDown/index.ts
index 68558561..e948926c 100644
--- a/packages/hooks/src/useCountDown/index.ts
+++ b/packages/hooks/src/useCountDown/index.ts
@@ -1,6 +1,6 @@
 import { useEffect, useMemo, useState } from 'react';
 import dayjs from 'dayjs';
-import usePersistFn from '../usePersistFn';
+import useMemoizedFn from '../useMemoizedFn';
 
 export type TDate = Date | number | string | undefined;
 
@@ -46,7 +46,7 @@ const useCountdown = (options?: Options) => {
   const [target, setTargetDate] = useState<TDate>(targetDate);
   const [timeLeft, setTimeLeft] = useState(() => calcLeft(target));
 
-  const onEndPersistFn = usePersistFn(() => {
+  const onEndPersistFn = useMemoizedFn(() => {
     if (onEnd) {
       onEnd();
     }
@@ -67,7 +67,7 @@ const useCountdown = (options?: Options) => {
       setTimeLeft(targetLeft);
       if (targetLeft === 0) {
         clearInterval(timer);
-          onEndPersistFn();
+        onEndPersistFn();
       }
     }, interval);
 
diff --git a/packages/hooks/src/useMemoizedFn/demo/demo1.tsx b/packages/hooks/src/useMemoizedFn/demo/demo1.tsx
new file mode 100644
index 00000000..36895182
--- /dev/null
+++ b/packages/hooks/src/useMemoizedFn/demo/demo1.tsx
@@ -0,0 +1,45 @@
+/**
+ * title: Default usage
+ * desc: useMemoizedFn is the same as useCallback.
+ *
+ * title.zh-CN: 基础用法
+ * desc.zh-CN: useMemoizedFn 与 useCallback 可以实现同样的效果。
+ */
+
+import React, { useState, useCallback, useRef } from 'react';
+import { message } from 'antd';
+import { useMemoizedFn } from 'ahooks';
+
+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
+        type="button"
+        onClick={() => {
+          setCount((c) => c + 1);
+        }}
+      >
+        Add Count
+      </button>
+      <div style={{ marginTop: 16 }}>
+        <button type="button" onClick={callbackFn}>
+          call callbackFn
+        </button>
+        <button type="button" onClick={memoizedFn} style={{ marginLeft: 8 }}>
+          call memoizedFn
+        </button>
+      </div>
+    </>
+  );
+};
diff --git a/packages/hooks/src/useMemoizedFn/demo/demo2.tsx b/packages/hooks/src/useMemoizedFn/demo/demo2.tsx
new file mode 100644
index 00000000..e8e34f46
--- /dev/null
+++ b/packages/hooks/src/useMemoizedFn/demo/demo2.tsx
@@ -0,0 +1,66 @@
+/**
+ * title: useMemoizedFn function reference will not change, which can be used for performance optimization.
+ * desc: In the example, `memoizedFn` reference will not change, `callbackFn` will change when count changes.
+ *
+ * title.zh-CN: useMemoizedFn 函数地址不会变化，可以用于性能优化
+ * desc.zh-CN: 示例中 `memoizedFn` 是不会变化的，`callbackFn` 在 count 变化时变化。
+ */
+
+import { useMemoizedFn } from 'ahooks';
+import { message } from 'antd';
+import React, { useCallback, useRef, useState } from 'react';
+
+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
+        type="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>
+    </>
+  );
+};
+
+// some expensive component with React.memo
+const ExpensiveTree = React.memo<{ [key: string]: any }>(({ showCount }) => {
+  const renderCountRef = useRef(0);
+  renderCountRef.current += 1;
+
+  return (
+    <div>
+      <p>Render Count: {renderCountRef.current}</p>
+      <button type="button" onClick={showCount}>
+        showParentCount
+      </button>
+    </div>
+  );
+});
diff --git a/packages/hooks/src/useMemoizedFn/index.en-US.md b/packages/hooks/src/useMemoizedFn/index.en-US.md
new file mode 100644
index 00000000..65a0c578
--- /dev/null
+++ b/packages/hooks/src/useMemoizedFn/index.en-US.md
@@ -0,0 +1,65 @@
+---
+title: useMemoizedFn
+nav:
+  title: Hooks
+  path: /hooks
+group:
+  title: Advanced
+  path: /advanced
+---
+
+# useMemoizedFn
+
+Hooks for persistent functions. In theory, useMemoizedFn can be used instead of useCallback.
+
+In some scenarios, we need to use useCallback to cache a function, but when the second parameter deps changes, the function will be regenerated, causing the function reference to change.
+
+```js
+const [state, setState] = useState('');
+
+// When the state changes, the func reference will change
+const func = useCallback(()=>{
+  console.log(state);
+}, [state]);
+```
+
+Using useMemoizedFn, you can omit the second parameter deps, and ensure that the function reference never change.
+
+```js
+const [state, setState] = useState('');
+
+// func reference nerver change
+const func = useMemoizedFn(()=>{
+  console.log(state);
+});
+```
+
+## Examples
+
+### Default usage
+
+<code src="./demo/demo1.tsx" />
+
+### Performance Improvement
+
+<code src="./demo/demo2.tsx" />
+
+## API
+
+```typescript
+type noop = (...args: any[]) => any;
+
+const fn = useMemoizedFn<T extends noop>(fn: T):T;
+```
+
+### Result
+
+| Property | Description                            | Type                      |
+|----------|----------------------------------------|---------------------------|
+| fn       | Fn the reference address never changes | `(...args: any[]) => any` |
+
+### Params
+
+| Property | Description                       | Type                      | Default |
+|----------|-----------------------------------|---------------------------|---------|
+| fn       | Function that require persistence | `(...args: any[]) => any` | -       |
diff --git a/packages/hooks/src/useMemoizedFn/index.ts b/packages/hooks/src/useMemoizedFn/index.ts
new file mode 100644
index 00000000..9bd86b09
--- /dev/null
+++ b/packages/hooks/src/useMemoizedFn/index.ts
@@ -0,0 +1,28 @@
+import { useMemo, useRef } from 'react';
+
+type noop = (...args: any[]) => any;
+
+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);
+
+  // why not write `fnRef.current = fn`?
+  // https://github.com/alibaba/hooks/issues/728
+  fnRef.current = useMemo(() => fn, [fn]);
+
+  const memoizedFn = useRef<T>();
+  if (!memoizedFn.current) {
+    memoizedFn.current = function (...args) {
+      return fnRef.current.apply(this, args);
+    } as T;
+  }
+
+  return memoizedFn.current;
+}
+
+export default useMemoizedFn;
diff --git a/packages/hooks/src/useMemoizedFn/index.zh-CN.md b/packages/hooks/src/useMemoizedFn/index.zh-CN.md
new file mode 100644
index 00000000..6a643de7
--- /dev/null
+++ b/packages/hooks/src/useMemoizedFn/index.zh-CN.md
@@ -0,0 +1,65 @@
+---
+title: useMemoizedFn
+nav:
+  title: Hooks
+  path: /hooks
+group:
+  title: Advanced
+  path: /advanced
+---
+
+# useMemoizedFn
+
+持久化 function 的 Hook，理论上，可以使用 useMemoizedFn 完全代替 useCallback。
+
+在某些场景中，我们需要使用 useCallback 来记住一个函数，但是在第二个参数 deps 变化时，会重新生成函数，导致函数地址变化。
+
+```js
+const [state, setState] = useState('');
+
+// 在 state 变化时，func 地址会变化
+const func = useCallback(()=>{
+  console.log(state);
+}, [state]);
+```
+
+使用 useMemoizedFn，可以省略第二个参数 deps，同时保证函数地址永远不会变化。
+
+```js
+const [state, setState] = useState('');
+
+// func 地址永远不会变化
+const func = useMemoizedFn(()=>{
+  console.log(state);
+});
+```
+
+## 代码演示
+
+### 基础用法
+
+<code src="./demo/demo1.tsx" />
+
+### 性能提升
+
+<code src="./demo/demo2.tsx" />
+
+## API
+
+```typescript
+type noop = (...args: any[]) => any;
+
+const fn = useMemoizedFn<T extends noop>(fn: T):T;
+```
+
+### Result
+
+| 参数 | 说明                      | 类型                      |
+|------|---------------------------|---------------------------|
+| fn   | 引用地址永远不会变化的 fn | `(...args: any[]) => any` |
+
+### Params
+
+| 参数 | 说明             | 类型                      | 默认值 |
+|------|------------------|---------------------------|--------|
+| fn   | 需要持久化的函数 | `(...args: any[]) => any` | -      |
diff --git a/packages/hooks/src/usePersistFn/demo/demo1.tsx b/packages/hooks/src/usePersistFn/demo/demo1.tsx
deleted file mode 100644
index d534e53d..00000000
--- a/packages/hooks/src/usePersistFn/demo/demo1.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * title: Default usage
- * desc: With usePersistFn, function references never change. In the example, `showCountPersistFn` will not change, `showCountCommon` will change when count changes.
- *
- * title.zh-CN: 基础用法
- * desc.zh-CN: 通过 usePersistFn，函数引用永远不会变化，示例中 `showCountPersistFn` 是不会变化的，`showCountCommon` 在 count 变化时变化。
- */
-
-import React, { useState, useCallback, useRef } from 'react';
-import { message } from 'antd';
-import { usePersistFn } from 'ahooks';
-
-export default () => {
-  const [count, setCount] = useState(0);
-
-  const showCountPersistFn = usePersistFn(() => {
-    message.info(`Current count is ${count}`);
-  });
-
-  const showCountCommon = useCallback(() => {
-    message.info(`Current count is ${count}`);
-  }, [count]);
-
-  return (
-    <>
-      <button
-        type="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 }}>
-        <h4>Component with persist function:</h4>
-        {/* use persist function, ExpensiveTree component will only render once */}
-        <ExpensiveTree showCount={showCountPersistFn} />
-      </div>
-      <div style={{ marginTop: 32 }}>
-        <h4>Component without persist function:</h4>
-        {/* without persist function, ExpensiveTree component will re-render on state change */}
-        <ExpensiveTree showCount={showCountCommon} />
-      </div>
-    </>
-  );
-};
-
-// some expensive component with React.memo
-const ExpensiveTree = React.memo<{ [key: string]: any }>(({ showCount }) => {
-  const renderCountRef = useRef(0);
-  renderCountRef.current += 1;
-
-  return (
-    <div>
-      <p>Render Count: {renderCountRef.current}</p>
-      <button type="button" onClick={showCount}>
-        showParentCount
-      </button>
-    </div>
-  );
-});
diff --git a/packages/hooks/src/usePersistFn/index.en-US.md b/packages/hooks/src/usePersistFn/index.en-US.md
deleted file mode 100644
index 04827a8f..00000000
--- a/packages/hooks/src/usePersistFn/index.en-US.md
+++ /dev/null
@@ -1,45 +0,0 @@
----
-title: usePersistFn
-nav:
-  title: Hooks
-  path: /hooks
-group:
-  title: Advanced
-  path: /advanced
----
-
-# usePersistFn
-
-Hooks for persistent functions.
-
-> Reference [How to read an often-changing value from useCallback?](https://reactjs.org/docs/hooks-faq.html#how-to-read-an-often-changing-value-from-usecallback)
->
-> In some rare cases you might need to memoize a callback with useCallback but the memoization doesn’t work very well because the inner function has to be re-created too often. For super-complex subcomponents, re-rendering can impact performance. With usePersistFn, you can guarantee that the function reference will never change.
-
-## Examples
-
-### Default usage
-
-<code src="./demo/demo1.tsx" />
-
-## API
-
-```typescript
-type noop = (...args: any[]) => any;
-
-const fn = usePersistFn<T extends noop>(
-  fn: T
-);
-```
-
-### Result
-
-| Property | Description                            | Type                      |
-|----------|----------------------------------------|---------------------------|
-| fn       | Fn the reference address never changes | `(...args: any[]) => any` |
-
-### Params
-
-| Property | Description                        | Type                      | Default |
-|----------|------------------------------------|---------------------------|---------|
-| fn       | Functions that require persistence | `(...args: any[]) => any` | -       |
diff --git a/packages/hooks/src/usePersistFn/index.ts b/packages/hooks/src/usePersistFn/index.ts
deleted file mode 100644
index 636521d5..00000000
--- a/packages/hooks/src/usePersistFn/index.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { useRef } from 'react';
-
-export type noop = (...args: any[]) => any;
-
-function usePersistFn<T extends noop>(fn: T) {
-  const fnRef = useRef<T>(fn);
-  fnRef.current = fn;
-
-  const persistFn = useRef<T>();
-  if (!persistFn.current) {
-    persistFn.current = function (...args) {
-      return fnRef.current!.apply(this, args);
-    } as T;
-  }
-
-  return persistFn.current!;
-}
-
-export default usePersistFn;
diff --git a/packages/hooks/src/usePersistFn/index.zh-CN.md b/packages/hooks/src/usePersistFn/index.zh-CN.md
deleted file mode 100644
index af287c09..00000000
--- a/packages/hooks/src/usePersistFn/index.zh-CN.md
+++ /dev/null
@@ -1,46 +0,0 @@
----
-title: usePersistFn
-nav:
-  title: Hooks
-  path: /hooks
-group:
-  title: Advanced
-  path: /advanced
----
-
-# usePersistFn
-
-持久化 function 的 Hook。
-
-> 参考 [如何从 useCallback 读取一个经常变化的值？](https://zh-hans.reactjs.org/docs/hooks-faq.html#how-to-read-an-often-changing-value-from-usecallback)
->
-> 在某些场景中，你可能会需要用 useCallback 记住一个回调，但由于内部函数必须经常重新创建，记忆效果不是很好，导致子组件重复 render。对于超级复杂的子组件，重新渲染会对性能造成影响。通过 usePersistFn，可以保证函数地址永远不会变化。
-
-
-## 代码演示
-
-### 基础用法
-
-<code src="./demo/demo1.tsx" />
-
-## API
-
-```typescript
-type noop = (...args: any[]) => any;
-
-const fn = usePersistFn<T extends noop>(
-  fn: T,
-);
-```
-
-### Result
-
-| 参数 | 说明                      | 类型                      |
-|------|---------------------------|---------------------------|
-| fn   | 引用地址永远不会变化的 fn | `(...args: any[]) => any` |
-
-### Params
-
-| 参数 | 说明             | 类型                      | 默认值 |
-|------|------------------|---------------------------|--------|
-| fn   | 需要持久化的函数 | `(...args: any[]) => any` | -      |
diff --git a/packages/hooks/src/useScroll/index.ts b/packages/hooks/src/useScroll/index.ts
index b0dbd578..279dabb5 100644
--- a/packages/hooks/src/useScroll/index.ts
+++ b/packages/hooks/src/useScroll/index.ts
@@ -1,5 +1,5 @@
 import { useEffect, useState } from 'react';
-import usePersistFn from '../usePersistFn';
+import useMemoizedFn from '../useMemoizedFn';
 import { BasicTarget, getTargetElement } from '../utils/dom';
 
 interface Position {
@@ -16,7 +16,7 @@ function useScroll(target?: Target, shouldUpdate: ScrollListenController = () =>
     top: NaN,
   });
 
-  const shouldUpdatePersist = usePersistFn(shouldUpdate);
+  const shouldUpdatePersist = useMemoizedFn(shouldUpdate);
 
   useEffect(() => {
     const el = getTargetElement(target, document);
diff --git a/packages/hooks/src/useTimeout/index.ts b/packages/hooks/src/useTimeout/index.ts
index f300b4ee..e80e9ad5 100644
--- a/packages/hooks/src/useTimeout/index.ts
+++ b/packages/hooks/src/useTimeout/index.ts
@@ -1,8 +1,8 @@
 import { useEffect } from 'react';
-import usePersistFn from '../usePersistFn';
+import useMemoizedFn from '../useMemoizedFn';
 
 function useTimeout(fn: () => void, delay: number | null | undefined): void {
-  const timerFn = usePersistFn(fn);
+  const timerFn = useMemoizedFn(fn);
 
   useEffect(() => {
     if (delay === undefined || delay === null) return;
diff --git a/packages/hooks/src/useWebSocket/index.ts b/packages/hooks/src/useWebSocket/index.ts
index 4044ee0e..9ee07705 100644
--- a/packages/hooks/src/useWebSocket/index.ts
+++ b/packages/hooks/src/useWebSocket/index.ts
@@ -1,5 +1,5 @@
 import useUnmount from '../useUnmount';
-import usePersistFn from '../usePersistFn';
+import useMemoizedFn from '../useMemoizedFn';
 
 import { useEffect, useRef, useState } from 'react';
 
@@ -50,7 +50,7 @@ export default function useWebSocket(socketUrl: string, options: Options = {}):
   /**
    * 重连
    */
-  const reconnect = usePersistFn(() => {
+  const reconnect = useMemoizedFn(() => {
     if (
       reconnectTimesRef.current < reconnectLimit &&
       websocketRef.current?.readyState !== ReadyState.Open
@@ -64,7 +64,7 @@ export default function useWebSocket(socketUrl: string, options: Options = {}):
     }
   });
 
-  const connectWs = usePersistFn(() => {
+  const connectWs = useMemoizedFn(() => {
     reconnectTimerRef.current && clearTimeout(reconnectTimerRef.current);
 
     if (websocketRef.current) {
@@ -101,7 +101,7 @@ export default function useWebSocket(socketUrl: string, options: Options = {}):
    * 发送消息
    * @param message
    */
-  const sendMessage: WebSocket['send'] = usePersistFn((message) => {
+  const sendMessage: WebSocket['send'] = useMemoizedFn((message) => {
     if (readyState === ReadyState.Open) {
       websocketRef.current?.send(message);
     } else {
@@ -112,7 +112,7 @@ export default function useWebSocket(socketUrl: string, options: Options = {}):
   /**
    * 手动 connect
    */
-  const connect = usePersistFn(() => {
+  const connect = useMemoizedFn(() => {
     reconnectTimesRef.current = 0;
     connectWs();
   });
@@ -120,7 +120,7 @@ export default function useWebSocket(socketUrl: string, options: Options = {}):
   /**
    * disconnect websocket
    */
-  const disconnect = usePersistFn(() => {
+  const disconnect = useMemoizedFn(() => {
     reconnectTimerRef.current && clearTimeout(reconnectTimerRef.current);
 
     reconnectTimesRef.current = reconnectLimit;


Test patch:
diff --git a/packages/hooks/src/useMemoizedFn/__tests__/index.test.ts b/packages/hooks/src/useMemoizedFn/__tests__/index.test.ts
new file mode 100644
index 00000000..32b98b31
--- /dev/null
+++ b/packages/hooks/src/useMemoizedFn/__tests__/index.test.ts
@@ -0,0 +1,38 @@
+import { act, renderHook, RenderHookResult } from '@testing-library/react-hooks';
+import { useState } from 'react';
+import useMemoizedFn from '../';
+
+const useCount = () => {
+  const [count, setCount] = useState(0);
+
+  const addCount = () => {
+    setCount((c) => c + 1);
+  };
+
+  const memoizedFn = useMemoizedFn(() => count);
+
+  return { addCount, memoizedFn };
+};
+
+let hook: RenderHookResult<[], ReturnType<typeof useCount>>;
+
+describe('useMemoizedFn', () => {
+  it('should be defined', () => {
+    expect(useMemoizedFn).toBeDefined();
+  });
+
+  it('useMemoizedFn should work', () => {
+    act(() => {
+      hook = renderHook(() => useCount());
+    });
+    const currentFn = hook.result.current.memoizedFn;
+    expect(hook.result.current.memoizedFn()).toEqual(0);
+
+    act(() => {
+      hook.result.current.addCount();
+    });
+
+    expect(currentFn).toEqual(hook.result.current.memoizedFn);
+    expect(hook.result.current.memoizedFn()).toEqual(1);
+  });
+});
diff --git a/packages/hooks/src/usePersistFn/__tests__/index.test.ts b/packages/hooks/src/usePersistFn/__tests__/index.test.ts
deleted file mode 100644
index e9852a16..00000000
--- a/packages/hooks/src/usePersistFn/__tests__/index.test.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { act, renderHook, RenderHookResult } from '@testing-library/react-hooks';
-import { useState } from 'react';
-import usePersistFn from '../';
-
-// 函数变化，但是地址不变
-
-const TestHooks = () => {
-  const [count, setCount] = useState(0);
-
-  const addCount = () => {
-    setCount((c) => c + 1);
-  };
-  const persistFn = usePersistFn(() => count);
-
-  return { addCount, persistFn };
-};
-
-let hook: RenderHookResult<[], ReturnType<typeof TestHooks>>;
-
-describe('usePersistFn', () => {
-  it('should be defined', () => {
-    expect(usePersistFn).toBeDefined();
-  });
-
-  it('usePersistFn should work', () => {
-    act(() => {
-      hook = renderHook(() => TestHooks());
-    });
-    const currentFn = hook.result.current.persistFn;
-    expect(hook.result.current.persistFn()).toEqual(0);
-
-    act(() => {
-      hook.result.current.addCount();
-    });
-
-    expect(currentFn).toEqual(hook.result.current.persistFn);
-    expect(hook.result.current.persistFn()).toEqual(1);
-  });
-});
diff --git a/packages/hooks/src/useUpdate/__tests__/index.test.ts b/packages/hooks/src/useUpdate/__tests__/index.test.ts
index 8683d137..b27c0d71 100644
--- a/packages/hooks/src/useUpdate/__tests__/index.test.ts
+++ b/packages/hooks/src/useUpdate/__tests__/index.test.ts
@@ -1,7 +1,7 @@
 import { useRef } from 'react';
 import { renderHook, act } from '@testing-library/react-hooks';
 import useUpdate from '..';
-import usePersistFn from '../../usePersistFn';
+import useMemoizedFn from '../../useMemoizedFn';
 
 describe('useUpdate', () => {
   it('should update', () => {
@@ -11,7 +11,7 @@ describe('useUpdate', () => {
       return {
         update,
         count: ref.current,
-        onChange: usePersistFn(() => {
+        onChange: useMemoizedFn(() => {
           ref.current = ref.current + 1;
           update();
         }),