Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React Hooks #88

Open
xiaotiandada opened this issue Oct 28, 2021 · 0 comments
Open

React Hooks #88

xiaotiandada opened this issue Oct 28, 2021 · 0 comments

Comments

@xiaotiandada
Copy link
Owner

xiaotiandada commented Oct 28, 2021

  1. 将完全不相关的 state 拆分为多组 state。
  2. 如果某些 state 是相互关联的,或者需要一起发生改变,就可以把它们合并为一组 state。
  3. 依赖数组依赖的值最好不要超过 3 个,否则会导致代码会难以维护。
  4. 如果发现依赖数组依赖的值过多,应该采取一些方法来减少它。
  • 去掉不必要的依赖。
  • 将 Hook 拆分为更小的单元,每个 Hook 依赖于各自的依赖数组。
  • 通过合并相关的 state,将多个依赖值聚合为一个。
  • 通过 setState 回调函数获取最新的 state,以减少外部依赖。
  • 通过 ref 来读取可变变量的值,不过需要注意控制修改它的途径。
  1. 应该使用 useMemo 的场景:
  • 保持引用相等
  • 成本很高的计算
  1. 无需使用 useMemo 的场景:
  • 如果返回的值是原始值: string, boolean, null, undefined, number, symbol(不包括动态声明的 Symbol),一般不需要使用 useMemo。
  • 仅在组件内部用到的 object、array、函数等(没有作为 props 传递给子组件),且没有用到其他 Hook 的依赖数组中,一般不需要使用 useMemo。
  1. Hooks、Render Props 和高阶组件都有各自的使用场景,具体使用哪一种要看实际情况。
  2. 若 Hook 类型相同,且依赖数组一致时,应该合并成一个 Hook。
  3. 自定义 Hooks 的返回值可以使用 Tuple 类型,更易于在外部重命名。如果返回的值过多,则不建议使用。
  4. ref 不要直接暴露给外部使用,而是提供一个修改值的方法。
  5. 在使用 useMemo 或者 useCallback 时,可以借助 ref 或者 setState callback,确保返回的函数只创建一次。也就是说,函数不会根据依赖数组的变化而二次创建。
import { useState, useMemo, useEffect, useCallback } from "react";

export const useCount = () => {
  const [count, setCount] = useState(0);

  const [increase, decrease] = useMemo(() => {
    console.log("memo");
    const increase = () => {
      console.log("click", count);

      setCount(count + 1);
    };

    const decrease = () => {
      setCount(count - 1);
    };
    return [increase, decrease];
  }, [count]);

  return [count, increase, decrease];
};

export const useCountNew = () => {
  const [count, setCount] = useState(0);

  const [increase, decrease] = useMemo(() => {
    console.log("memo new");

    const increase = (val: number) => {
      console.log("v", val);
      setCount((latestCount) => latestCount + val);
    };

    const decrease = (val: number) => {
      console.log("v1", val);

      setCount((latestCount) => latestCount - val);
    };
    return [increase, decrease];
  }, []); // 保持依赖数组为空,这样 increase 和 decrease 方法都只会被创建一次

  const increaseCall = useCallback((val: number) => {
    console.log("increaseCall val", val);
    setCount((latestCount) => latestCount + val);
  }, []);

  return [count, increase, decrease, increaseCall];
};

function Counter() {
  const [count, increase] = useCount();

  useEffect(() => {
    const handleClick = () => {
      console.log("Counter Click");
      increase(); // 执行后 count 的值永远都是 1
    };

    document.body.addEventListener("click", handleClick);
    return () => {
      document.body.removeEventListener("click", handleClick);
    };
  }, []);

  return <h1>{count}</h1>;
}

export default function App() {
  const [count1, increase1, decrease1] = useCount();
  const [count, increase, decrease, increaseCall] = useCountNew();

  return (
    <div className="App" style={{ textAlign: "center" }}>
      <h1>Hello CodeSandbox</h1>
      <p>{count1}</p>
      <button onClick={() => increase1()}>increase</button>
      <button onClick={() => decrease1()}>decrease</button>

      <hr />
      <p>{count}</p>
      <button onClick={() => increase(count1)}>increase</button>
      <button onClick={() => decrease(count1)}>decrease</button>
      <button onClick={() => increaseCall(count1)}>decrease</button>

      <hr />
      <Counter />
    </div>
  );
}

import { useEffect, useState } from "react";
import { render } from "react-dom";

let _state: any;
let _deps: any;
let memorizedState: any[] = [];
let cursor: number = 0;

function useStateCustom(initialValue: any) {
  _state = _state || initialValue;
  function setState(newState: any) {
    console.log("setState", newState);
    _state = newState;
    renderFn();
  }
  return [_state, setState];
}

function useStateCustomMemo(initialValue: any) {
  memorizedState[cursor] = memorizedState[cursor] || initialValue;
  const currentCursor = cursor;
  function setState(newState: any) {
    // console.log("setState memo", newState, currentCursor, memorizedState);
    memorizedState[currentCursor] = newState;
    renderFn();
  }

  return [memorizedState[cursor++], setState];
}

function useEffectCustom(callback: Function, depArray: any[]) {
  const hasNoDeps = !depArray; // not dependencies
  const hasChangeDeps = _deps
    ? !depArray.every((el, i) => el === _deps[i])
    : true; // 两次的 dependencies 是否完全相等
  if (hasNoDeps || hasChangeDeps) {
    callback();
    _deps = depArray;
  }
}

function useEffectCustomMemo(callback: Function, depArray: any[]) {
  const hasNoDeps = !depArray;
  const deps = memorizedState[cursor];
  const hasChangeDeps = deps
    ? !depArray.every((el, i) => el === deps[i])
    : true;

  if (hasNoDeps || hasChangeDeps) {
    callback();
    memorizedState[cursor] = depArray;
  }

  cursor++;
}

export default function App() {
  const [count, setCount] = useStateCustom(0);
  const [count1, setCount1] = useState(0);

  const [countMemo, setCountMemo] = useStateCustomMemo(0);
  const [countMemo1, setCountMemo1] = useStateCustomMemo(0);

  // useEffect(() => {
  //   console.log("test useCallback");
  // }, []);

  // useEffectCustom(() => {
  //   console.log("useEffectCustom");
  // }, []);

  // useEffectCustom(() => {
  //   console.log("useEffectCustom count");
  // }, [count]);

  useEffectCustomMemo(() => {
    console.log("useEffectCustom countMemo");
  }, [countMemo]);

  useEffectCustomMemo(() => {
    console.log("useEffectCustom countMemo1");
  }, [countMemo1]);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <span>custom: {count}</span>
      <button onClick={() => setCount(count + 1)}>+</button>
      <hr />
      <span>custom memo: {countMemo}</span>
      <button onClick={() => setCountMemo(countMemo + 1)}>+</button>
      <span>custom memo: {countMemo1}</span>
      <button onClick={() => setCountMemo1(countMemo1 + 1)}>+</button>
      <hr />
      <span>default: {count1}</span>
      <button onClick={() => setCount1(count1 + 1)}>+</button>
    </div>
  );
}

const rootElement = document.getElementById("root");

function renderFn() {
  cursor = 0;
  render(<App />, rootElement);
}

renderFn();

https://codesandbox.io/s/react-hooks-ytbtb?file=/src/index.tsx

依靠 memorizedState 数组来存放数据

@xiaotiandada xiaotiandada reopened this Nov 25, 2022
@xiaotiandada xiaotiandada changed the title React Hooks 学习 React Hooks Nov 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant