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 文件中
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 };
使用示例:
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')
}
打印结果:
修改一次: 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的值,一直都是最新的,相当于同步修改了。