React Hooks 的分析 - useSetState
- Published on
- — 519 Words
- Authors
- Name
- Richard Shih
- @realshiddong
react-use 与 ahooks 中对 useSetState 的实现方式很相似。
react-use
代码实现:useSetState
文档/Demo:useSetState
入参上,setState
支持传入函数或一个对象值,内部通过 Object.assign()
方法浅层合并数据,整体逻辑非常简单。
import { useCallback, useState } from 'react'
const useSetState = <T extends object>(
initialState: T = {} as T
): [T, (patch: Partial<T> | ((prevState: T) => Partial<T>)) => void] => {
const [state, set] = useState<T>(initialState)
const setState = useCallback((patch) => {
set((prevState) =>
Object.assign({}, prevState, patch instanceof Function ? patch(prevState) : patch)
)
}, [])
return [state, setState]
}
export default useSetState
ahooks
代码实现:useSetState
文档/Demo:useSetState
与 react-use 基本相同,setMergeState
的入参也支持传入函数或一个对象值,只不过内部通过是通过对象拓展运算符进行浅层合并数据。
接口定义:
export type SetState<S extends Record<string, any>> = <K extends keyof S>(
state: Pick<S, K> | null | ((prevState: Readonly<S>) => Pick<S, K> | S | null)
) => void
代码实现:
const useSetState = <S extends Record<string, any>>(
initialState: S | (() => S)
): [S, SetState<S>] => {
const [state, setState] = useState<S>(initialState)
const setMergeState = useCallback((patch) => {
setState((prevState) => {
const newState = isFunction(patch) ? patch(prevState) : patch
return newState ? { ...prevState, ...newState } : prevState
})
}, [])
return [state, setMergeState]
}
export default useSetState
总结
react-use 与 ahooks 对于这个 hook 的实现只有细微处理上的差别,大同小异。
另外,在处理不可变数据时,使用 immer 是一个不错的选择,它简化了不可变数据结构的处理。 同时 immer 也为 React 中也提供了一个 useImmer hook,下面是一个使用 demo,
import React, { useCallback } from "react";
import { useImmer } from "use-immer";
const TodoList = () => {
const [todos, setTodos] = useImmer([
{
id: "React",
title: "Learn React",
done: true
},
{
id: "Immer",
title: "Try Immer",
done: false
}
]);
const handleToggle = useCallback((id) => {
setTodos((draft) => {
const todo = draft.find((todo) => todo.id === id);
todo.done = !todo.done; // 可变数据的修改方式:直接修改数据属性
});
}, []);
const handleAdd = useCallback(() => {
setTodos((draft) => {
draft.push({
id: "todo_" + Math.random(),
title: "A new todo",
done: false
});
});
}, []);
经过 immer 的封装之后,可以像使用 setState
一样自如的使用 useImmer
,同时可以做到可变数据的修改方式。