ahooks 源码解读系列 - 2

183 阅读2分钟

useSetState

ahooks官网链接

💡 Tips:管理对象类型的 state

场景

当我们想修改对象里的某个属性,而不影响其他属性,可以方便操作对象类型的 state

比如:传递给后端的数据用 object保存,当 setState 一个值,不影响其他值,就可以用这个 hook

案例

demo

import React from "react";
import { useSetState } from "./hooks";

export default function App() {
    const [state, setState] = useSetState({
        name: "jack",
        age: 18
    });

    const onClick = () => {
        setState({ age: 20 });
    };

    return (
        <>
            <button onClick={onClick}>点我修改age</button>
            <div className="App">{JSON.stringify(state, null, 2)}</div>
        </>
    );
}

核心代码

import { useCallback, useState } from "react";
const isFunction = (value: unknown): value is Function => typeof value === "function";

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;

export 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];

};

useToggle

参考链接

💡 Tips:管理在两个值之间切换的值

场景

model 框的控制显示隐藏值, 可以通过此 hook 方便管理

案例

demo

2022-09-29 15.08.48.gif

import useToggle from "./hooks";

export default function App() {
  const [state, actions] = useToggle("hello", "world");
  const { toggle, set, setLeft, setRight } = actions;

  return (
    <>
      <button onClick={toggle}>toggle</button>
      <button onClick={() => set("heihei")}>set</button>
      <button onClick={setLeft}>setLeft</button>
      <button onClick={setRight}>setRight</button>
      state: {state}
    </>
  );
}

核心代码

import { useMemo, useState } from 'react';

export interface Actions<T> {
  setLeft: () => void;
  setRight: () => void;
  set: (value: T) => void;
  toggle: () => void;
}

function useToggle<T = boolean>(): [boolean, Actions<T>];

function useToggle<T>(defaultValue: T): [T, Actions<T>];

function useToggle<T, U>(defaultValue: T, reverseValue: U): [T | U, Actions<T | U>];

function useToggle<D, R>(defaultValue: D = false as unknown as D, reverseValue?: R) {
  const [state, setState] = useState<D | R>(defaultValue);

  const actions = useMemo(() => {
    // 获取reverse 的值,如果没给这个值,则取defaultValue的反值;
    const reverseValueOrigin = (reverseValue === undefined ? !defaultValue : reverseValue) as D | R;

    const toggle = () => setState((s) => (s === defaultValue ? reverseValueOrigin : defaultValue));
    const set = (value: D | R) => setState(value);
    const setLeft = () => setState(defaultValue);
    const setRight = () => setState(reverseValueOrigin);

    return {
      toggle,
      set,
      setLeft,
      setRight,
    };
    // useToggle ignore value change
    // }, [defaultValue, reverseValue]);
  }, []);

  return [state, actions];
}

export default useToggle;

useBoolean

💡 Tips:优雅的管理布尔值。

场景

优雅管理 drawer 的显示隐藏

案例

demo

import useBoolean from "./useBoolean";

export default function App() {
  const [visible, actions] = useBoolean(false);
  const { toggle } = actions;
  return (
    <div className="App">
      <button onClick={toggle}>toggle</button>
      {visible && <h1>Hello CodeSandbox</h1>}
    </div>
  );
}

核心代码

import { useMemo } from 'react';
import useToggle from '../useToggle';

export interface Actions {
  setTrue: () => void;
  setFalse: () => void;
  set: (value: boolean) => void;
  toggle: () => void;
}

export default function useBoolean(defaultValue = false): [boolean, Actions] {
  const [state, { toggle, set }] = useToggle(defaultValue);

  const actions: Actions = useMemo(() => {
    const setTrue = () => set(true);
    const setFalse = () => set(false);
    return {
      toggle,
      set: (v) => set(!!v),
      setTrue,
      setFalse,
    };
  }, []);

  return [state, actions];
}