state相关hook清单
根据梳理,目前的ahooks版本3.7.7提供了state相关的hook共计16个,我们会分几个篇章来分别看下,这篇我们分析下useSetState
和useState
一些基本要点
前置知识点
useState
我们在看ahooks中state相关hook前,还是在回顾下React中基础的useState的使用
定义
useState
is a React Hook that lets you add a state variable to your component.
说人话就是 useState能给函数式组件引入状态变量,因为纯函数组件本身,在没有hook的加持下,只支持根据Props的联动UI展示
基本使用
const [state, setState] = useState(initData)
参数
- initialState:是一个你希望的初始值,可以是任何类型,但是如果是函数的话,它的参数将会被忽略
- 如果initialState是函数,应当是个没有参数的
纯函数
.React会以函数的返回值作为初始State
- 如果initialState是函数,应当是个没有参数的
返回值
数组形式,第一个参数是state,第二个参数是set函数,不再赘述
注意点
除了hook使用的基本规定,就是只能在顶层函数使用,不能在if,循环中使用外,在严格模式的开发模式中,initial的function会调用两次,目的就是看下这个function是不是纯函数,当然这个特性不会在production模式中生效。其实目的也很简单,就是帮助我们发现一些因为不是纯函数导致的问题
简单讲下纯函数的定义
function A(a,b){
return a+b
}
// 对于特定的函数A,只要参数列表固定,多次调用该函数,返回值应当是一样的
// A(1,2);
// A(1,2);
// A(1,2);
哪些情况就不是纯函数了,比如内部有随机数,异步获取IO【网络,系统API】等等,因为这些会导致相同的函数,在不同的时刻或者条件下返回的值是不一样的
useState
使用和其他问题就不再赘述了,大家可以看下官网定义或者其他博主的分享
useSetState
作用
管理 object 类型 state 的 Hooks,用法与 class 组件的
this.setState
基本一致。
凡事先说干嘛用的,这里有个非常值得注意的点,就是有了useState,ahook为什么还要封装个useSetState,这个Set的含义是什么
究其原因,我们根据ahook的定义跟Class 模式下的this.setState对比下
// 伪代码
const [a,setA]=useState({name:'张三',age:14});
// 如果我们在某个地方,想把age改成18,setA({age:18})的话 会发生什么??
import React from 'react';
import { useSetState } from 'ahooks';
interface State {
hello: string;
[key: string]: any;
}
export default () => {
const [state, setState] = useSetState<State>({
hello: '',
});
const [state1,setState1]=React.useState({name:'s',age:14} as any);
return (
<div>
<pre>{JSON.stringify(state, null, 2)}</pre>
<pre>{JSON.stringify(state1, null, 2)}</pre>
<p>
<button type="button" onClick={() =>
setState({hello: 'world'})}>
set hello
</button>
<button type="button" onClick={() => setState({ foo: 'bar' })} style={{ margin: '0 8px' }}>
set foo
</button>
<button onClick={()=> setState1({age: 18})}>测试useState</button>
</p>
</div>
);
};
结果说下,就是a整个就变成了{age:18},而不是this.setState后的{name:'张三',age:18},这个就跟this.setState区别的就在于少了一个针对State的Merge
操作
然后我们再来看useSetState的源码就很清楚了
import { useCallback, useState } from 'react';
import { isFunction } from '../utils';
// useSetState的第二个返回值,set操作函数的ts类型声明
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;
// Record,ts内建键值对式类型工具
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) => {
// 判断set动作的参数是否为函数
const newState = isFunction(patch) ? patch(prevState) : patch;
// 进行merge操作
return newState ? { ...prevState, ...newState } : prevState;
});
}, []);
return [state, setMergeState];
};
export default useSetState;
因此我们可以总结下useSetState
可以让我们将一类状态变量放到一个state中,避免多次使用useState创建多个状态变量和对应的更新操作
下篇预告
接下来我们读源码的进度会尽量加快一些,下一篇介绍下useSet
和useMap
的使用和源码,顺便巩固下ES6中set和map这两种数据结构