useState使用时的几个痛点
1、更改某一个值时不方便
const [state, setState] = useState({a:1, b:{c: 2}})
// 更改a
setState(s=>{
return {...s, a: 2}
})
// 更改c
setState(s=>{
return {
...s,
b: {
...s.b,
c:3
}
}
})
希望这样使用
// 更改a
setState({a: 2})
// 更改c
setState(s=>{
s.b.c=3;
})
2、更改一个值后,无法立即取到最新值
react本身就没有提供获取到最新值的方法
const [state, setState] = useState({a:1, b:{c: 2}})
const demo = () => {
setState(s=>{
return {...s, a: 2}
})
console.log(state.a) // => 1
}
希望
const [state, setState] = useState({a:1, b:{c: 2}})
const demo = async () => {
const newState = await setState(s=>{
return {...s, a: 2}
})
console.log(newState.a) // => 2
}
// 或者 通过回调
setState(s=>{
return {...s, a: 2}
}, newState => {
console.log(newState.a) // => 2
})
3、希望useState可以重置值
在弹窗关闭重置数据时 非常有用
const [state, setState, reset] = useState({
a: 1,
b: { c: 2 }
})
// 获取所有最新值,且不会触发重新渲染
const newState = await setState()
// 是否还可以还原为初始值
reset() // 全部还原
reset("a") // 还原具体一项
reset(["a", ""]) // 还原指定具体项
实现
import { useState, useCallback } from "react";
import immer from "immer";
type SetStateAction<T> = Partial<T> | ((prevState: T) => void);
type Callback<T> = (values: T) => void
type Dispatch<T> = (values?: SetStateAction<T>, cb?: Callback<T>) => Promise<T>;
type Reset<T> = (valueKeys?: keyof T | (keyof T)[]) => void
/**
* 批量处理更新
* @param initialValue 对象类型
* @returns [values, dispatch, reset]
*/
function useValues<T extends Record<string, any>>(initialValue: T): [T, Dispatch<T>, Reset<T>] {
const [values, dispatch] = useState<T>(initialValue);
const setDispatch = useCallback((_values?: SetStateAction<T>, cb?: Callback<T>) => {
return new Promise<T>((resolve, reject) => {
try {
if (typeof _values === "function") {
dispatch((state: T) => {
const newValues = immer(state, _values)
if (typeof cb === 'function') {
Promise.resolve(newValues).then(cb)
}
resolve(newValues)
return newValues
});
} else if (typeof _values === "object") {
dispatch((values: T) => {
const newValues = { ...values, ..._values }
if (typeof cb === 'function') {
Promise.resolve(newValues).then(cb)
}
resolve(newValues)
return newValues
})
// 获取全部值
} else if (_values == null && typeof initialValue === 'object') {
dispatch((values: T) => {
resolve(values)
return values
})
}
} catch (error) {
reject(error)
}
})
}, [dispatch]);
const reset = useCallback((valueKeys?: keyof T | (keyof T)[]) => {
const initialKey = Object.keys(initialValue)
// 全部重置
if (valueKeys == null) {
dispatch(() => initialValue)
// 根据数组中字段,对应重置
} else if (Array.isArray(valueKeys)) {
dispatch((state: T) => {
const resetInitialValue = valueKeys.reduce((prev, next) => {
if (initialKey.includes(next as string)) {
prev[next] = initialValue[next]
}
return prev
}, {} as T)
return {
...state,
...resetInitialValue
}
})
// 指定字段重置
} else if (typeof valueKeys === 'string') {
const isExit = initialKey.includes(valueKeys)
if (!isExit) {
return
}
dispatch((state: T) => ({
...state,
[valueKeys]: initialValue[valueKeys]
}))
}
}, [])
return [values, setDispatch, reset];
}
export default useValues;
注意事项:切记不要在useEffect中返回 useValues,因为useValues是一个函数
// 错误用法
useEffect(()=>{
return useValues()
}, [])