Skip to content

state相关hook清单

根据梳理,目前的ahooks版本3.7.7提供了state相关的hook共计16个,我们会分几个篇章来分别看下,这篇我们分析下useSetStateuseState一些基本要点

image-20231211143323717

前置知识点

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展示

基本使用

jsx
const [state, setState] = useState(initData)

参数

  • initialState:是一个你希望的初始值,可以是任何类型,但是如果是函数的话,它的参数将会被忽略
    • 如果initialState是函数,应当是个没有参数的纯函数.React会以函数的返回值作为初始State

返回值

数组形式,第一个参数是state,第二个参数是set函数,不再赘述

注意点

除了hook使用的基本规定,就是只能在顶层函数使用,不能在if,循环中使用外,在严格模式的开发模式中,initial的function会调用两次,目的就是看下这个function是不是纯函数,当然这个特性不会在production模式中生效。其实目的也很简单,就是帮助我们发现一些因为不是纯函数导致的问题

简单讲下纯函数的定义

js
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对比下

jsx
// 伪代码
const [a,setA]=useState({name:'张三',age:14});

// 如果我们在某个地方,想把age改成18,setA({age:18})的话 会发生什么??
tsx
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>
  );
};

image-20231211152816305

结果说下,就是a整个就变成了{age:18},而不是this.setState后的{name:'张三',age:18},这个就跟this.setState区别的就在于少了一个针对State的Merge操作

然后我们再来看useSetState的源码就很清楚了

tsx
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创建多个状态变量和对应的更新操作

下篇预告

接下来我们读源码的进度会尽量加快一些,下一篇介绍下useSetuseMap的使用和源码,顺便巩固下ES6中set和map这两种数据结构

关注我,敬请期待