React Hooks 实现同步useState(在setState之后的代码直接获取到最新的值)

578 阅读2分钟

众所周知React的useState在set某个值之后虽然视图回刷新,但是无法直接在js中获取到最新的值(更新是异步的),无法实现类似于赋值后回调的操作。但是useRef的特性正好跟useState相反,是可以直接在js中同步更新但是视图不会刷新

简单来说就是组合useState和useRef,制造一个新的hooks来替代useState,在jsx这种照样像useState一样使用,但是在需要做同步代码(回调)的时候使用ref去读取,保证能获取到最新的值。

该方案造成的性能影响未知,但感知上几乎可忽略不计。

直接上代码

import { MutableRefObject, useRef, useState } from "react";

/**
 * 定义一个同步状态管理的钩子,用于在React组件中同步管理状态
 * 它提供了初始状态设置、状态更新和状态引用的功能
 */
export type UseSyncState = <T = any>(
  initialValue: T,
) => [T, (input: T) => void, MutableRefObject<T>];

/**
 * 实现useSyncState钩子
 * @param initialValue 初始状态值
 * @returns 返回一个包含当前状态、更新状态函数和状态引用的数组
 */
export const useSyncState: UseSyncState = <T = any,>(initialValue: T) => {
  // 使用useState钩子管理组件的状态
  const [state, setState] = useState(initialValue);

  // 使用useRef钩子创建一个状态的ref,用于在组件更新时保持对当前状态的引用
  const stateRef = useRef<T>(state);
    
  /**
   * 同步更新状态的函数,支持传入新的状态值或一个函数,该函数接收上一个状态并返回新的状态
   * @param v
   */
  const syncSetState: (input: T) => void = (v) => {
    setState(v);
    stateRef.current = v;
  };

  // 返回当前状态、更新状态的函数和状态的ref
  return [state, syncSetState, stateRef];
};

如何使用

import { useSyncState } from "@/utils/hooks/useSyncState";
import { Button, Space } from "antd";

export const Test: React.FC = () => {
  const [count, setCount, countRef] = useSyncState(0);

  const handleAddCount = () => {
    setCount(countRef.current + 1); //直接能获取到最新的值进行计算
    console.log(countRef.current); // 直接能打印出最新的值
  };

  return (
    <div>
      <Space>
        {/* 视图会显示出最新的值 */}
        {count}
        <Button onClick={handleAddCount}></Button>
      </Space>
    </div>
  );
};

export default Test;

ps:因为永远可以获取到最新的值,则setState函数不再需要通过函数的方式传入,所以没做这部分兼容。