编程崽

登录

一叶在编程苦海沉沦的扁舟之上,我是那只激情自射的崽

同步修改变量功能封装 useVal for react

同步修改变量功能封装 useVal for react

react 正常 useState 功能,并不是同步修改state,再某些场景需要借助 useRef 才能完成功能。

比如调用某个接口拿了数据A,需要进行某些计算时,需要用到组件中另一个数据B的最新的值,这个功能就不能顺利完成。

要么把数据A也方法一个useState中,然后使用useEffect监听数据A和数据B,进行计算。

要么使用一个数据B的ref,修改数据B时需要手动或监听数据B的变化,修改数据B的ref值,在接口完成后,从数据B的ref中读取最新的数据B。

就很难受。

所以封装了下面这个js,用法和useState相同,只是会多返回一个getRef的方法,使用getRef可以读取到最新的值,下面有使用示例。

useVal.ts 文件中

ts 复制代码
import { useState, useRef, useCallback } from 'react';

// 重载函数签名,以支持 T 或 T | undefined 类型的初始值
function useVal<T>(): [T | undefined, React.Dispatch<React.SetStateAction<T | undefined>>, () => T | undefined];
function useVal<T>(initialValue: T): [T, React.Dispatch<React.SetStateAction<T>>, () => T];
function useVal<T>(initialValue: T | (() => T)): [T, React.Dispatch<React.SetStateAction<T>>, () => T];

function useVal<T>(initialValue?: T | (() => T)): [T | undefined, React.Dispatch<React.SetStateAction<T | undefined>>, () => T | undefined] {
  // 使用 useState 来维护可响应的数据
  // 这里不再强制类型转换,而是让 TypeScript 自动推断
  const [data, setStateData] = useState(initialValue);

  // 使用 useRef 来维护一个可以获取最新值的引用
  // useRef 的初始值也保持与 useState 一致
  const dataRef = useRef(initialValue instanceof Function ? initialValue() : initialValue);

  // 封装 setStateData,在更新 state 的同时更新 ref
  const setData = useCallback((newValue: React.SetStateAction<T | undefined>) => {
    setStateData(prevData => {
      // 计算最新的值
      const finalValue = typeof newValue === 'function'
        ? (newValue as (prevState: T | undefined) => T | undefined)(prevData)
        : newValue;

      // 同步更新 ref
      dataRef.current = finalValue;

      return finalValue;
    });
  }, []);

  // 返回一个函数,用于获取 dataRef.current 的最新值
  const getData = useCallback(() => dataRef.current, []);

  // 这里的返回类型需要统一为 T | undefined,因为存在不传初始值的情况
  return [data, setData, getData];
}

export { useVal };

使用示例:

ts 复制代码
import { useEffect, useState } from 'react'
import { useVal } from '@/utils/useVal'

export default function() {
  const [a, setA] = useState<number>(1)
  const [b, setB, getB] = useVal<number>(1)

  useEffect(() => {
    setInterval(() => {
      setA(old => old + 1)
      setB(old => old + 1)
      console.log(`修改一次:  a: ${a}, b: ${b}, b的ref: ${getB()}`)
    }, 1000)
  }, [])

  return ('Test')
}

打印结果:

sh 复制代码
修改一次:  a: 1, b: 1, b的ref: 2
修改一次:  a: 1, b: 1, b的ref: 3
修改一次:  a: 1, b: 1, b的ref: 4
修改一次:  a: 1, b: 1, b的ref: 5

可以看到,因为react Hook的特性,打印中a和b的值都是1,而b的ref的值,一直都是最新的,相当于同步修改了。

更新时间:2025/07/09 10:49:42