分析的源码来自https://github.com/alibaba/ho...
首先,ahook中文文档界面对useSetState的说明为:管理 object 类型 state 的 Hooks,用法与 class 组件的 this.setState 基本一致。
相似方法useState、this.setState的基本介绍
useState的基本用法
const [state, setState] = useState(initialState);//state初始赋值为initialState
setState(newState);//修改state为newState
作为react的基础hook,useState中的state参数只在第一次渲染时被初始化,之后重新渲染并不会改变它本身的值,而每一次使用setState改变state都将引起组件的重新渲染。
class组件中的this.setState用法
(该例子来源于react文档)
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {//初始状态
count: 0
};
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
//增加或修改状态
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}
this.setState与useState不同的是,它规定的state为键值对类型是string:any的对象。调用this.setState()是在state中新增状态(当然,状态若同名即为修改,比如上述例子)。
this.setState的局限在于它仅能在class组件中使用,而我们现在用得更多的是函数组件。
useSetState的用法
useSetState的简单使用方法是:设置类型为对象(即string-any键值对)的状态,并在初始化的时候传入初始参数,之后调用setState(或者取其他的名字)来新增或修改状态,状态更新将导致重新渲染。
const [state, setState] = useSetState<类型>({
hello: '',
count: 0,
});
这里是一些基础的源码分析。
import { useCallback, useState } from 'react';
import { isFunction } from '../utils';
//为等号后面的类型取一个别名: 类型SetState,S被约束自自映射类型{string:any}。
//被取名的类型:(泛型k被约束为s的属性名)函数
//参数state的类型:Pick<S, K>(即在S类型中取某属性K)或null,或返回前面两种类型或S类型的函数。
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;
//首先规定了传入参数的泛型为S被约束自自映射类型{string:any}。
//传入的参数为新的state,或一个输出参数是新state的函数。
//返回的参数为 [S, SetState<S>] 。
const useSetState = <S extends Record<string, any>>(
initialState: S | (() => S),
): [S, SetState<S>] => {
//使用了react的hook:useState来包装。
const [state, setState] = useState<S>(initialState);
//使用了一个useCallback,返回一个 memoized 回调函数(使用相同参数时会直接返回结果)
//封装了合并新旧state的操作。
const setMergeState = useCallback((patch) => {
setState((prevState) => {
const newState = isFunction(patch) ? patch(prevState) : patch;
//扩展符的作用是保持原有的数据不变
return newState ? { ...prevState, ...newState } : prevState;
});
}, []);
return [state, setMergeState];
};
export default useSetState;
useSetState内部首先使用了一个useState来包装传入的state。在上面的分析中可以得知,useState的用途是直接将state变更为新的内容,与useSetState在state中添加新属性的用法不符合。因此,useSetState的做法是:首先使用一个useCallback包装,接着使用useState传入一个函数,在函数内部完成合并操作,返回更新过后的state。
要点分析:使用useCallback的原因
官方文档中对useCallback的说明为:把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。此处使用的useCallback第二个参数为空数组,也就是说这个useCallback只会在挂载的时候执行。
此处使用的useCallback内部调用的useState的参数不是state的新值,而是一个回调函数,因此取到的state不是闭包中获取的,而是从回调函数参数中获取,可以正常发挥合并作用(若直接传入合并后的新state,则旧state的值会为initialState)。
而使用useCallback会返回该回调函数的 memoized 版本,也就是说当参数不发生改变的时候函数不会重新调用,有助于避免每次重新渲染都要进行高开销的计算。
参考链接:
1、react中文文档https://react.docschina.org/d...
2、ahooks中文文档https://ahooks.gitee.io/zh-CN...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。