登录
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';
import type { Dispatch, SetStateAction } from 'react';
// 可以当做 useState 用,但数组会多传回一个方法,使用方法可以实时获取最新的值
function useVal<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>, (() => S)] {
let initData = typeof initialState === 'function' ? (initialState as (() => S))() : initialState
// 内部使用 useState
const [data, setData] = useState<S>(() => initData)
// 创建一个 useRef,用来实时给外部提供最新的值
const dataRef = useRef(initData)
// 供外部修改值的方法,方法中会同时给 ref 赋值,供外部随时获取最新值
const setDataExport = useCallback((params: S | ((prevState: S) => S)) => {
// 这里的旧值从ref中取
let oldData = dataRef.current
// 放置新的值的变量
let newData = oldData
// 如果传入的是方法,这里执行得到结果
if (typeof params === 'function') newData = (params as ((prevState: S) => S))(oldData)
// 不是方法,直接赋值
else newData = params
// 先更新ref
dataRef.current = newData
// 再真的去更新 useState 记录的值,这里需要方法返回新值的方式
setData(() => newData)
}, [])
// 供外部获取实时的值的方法
const getRef = useCallback((): S => dataRef.current, [])
return [data, setDataExport, getRef]
}
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的值,一直都是最新的,相当于同步修改了。