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有哪些? #27

Open
HCLacids opened this issue Feb 25, 2022 · 8 comments
Open

React:hooks有哪些? #27

HCLacids opened this issue Feb 25, 2022 · 8 comments
Labels

Comments

@HCLacids
Copy link
Owner

参考链接
useState、useEffect、useCallBack、useMemo、useContext、useReducer、useRef、useLayoutEffect
useLayoutEffect:用法和useEffect一致,与useEffect的差别是执行时机,useLayoutEffect是在浏览器绘制节点之前执行(和componentDidMount以及componentDidUpdate执行时机相同)
类组件有着耦合性高的缺点,数据之间和功能部分的重复较高,数据问题Redux和flux解决了问题。hooks的出现就是为了解决功能复用的问题。
Function Component和Class Component
Function组件是对于无状态组件的思考,什么样的组件不需要state,只需要props进行数据之间的通讯。
Hooks带给了Function组件的状态管理化
useState带给了function的state。
useState为啥不返回object而是返回tuple?
如果 useState 返回数组,那么你可以顺便对数组中的变量命名,代码看起来也比较干净。而如果是对象的话返回的值必须和 useState 内部实现返回的对象同名,这样你只能在 function component 中使用一次,想要多次使用 useState 必须得重命名返回值。
为什么必须在函数组件顶部作用域调用Hooks API
在函数组件中,memoizedState被设计成一个链表(Hook对象):

function App() {
  const [ n1, setN1 ] = useState(1);
  const [ n2, setN2 ] = useState(2);

  // if (sth) {
  //    const [ n4, setN4 ] = useState(4);
  // } else {
  //    const [ n5, setN5 ] = useState(5);
  // }

  const [ n3, setN3 ] = useState(3);
}

pic
Hook API调用会产生一个对应的Hook实例(并追加到Hooks链),但是返回给组件的是state和对应的setter,re-render时框架并不知道这个setter对应哪个Hooks实例(除非用HashMap来存储Hooks,但这就要求调用的时候把相应的key传给React,会增加Hooks使用的复杂度)。
re-render时会从第一行代码开始重新执行整个组件,即会按顺序执行整个Hooks链,如果re-render时sth不满足,则会执行useState(5)分支,相反useState(4)则不会执行到,导致useState(5)返回的值其实是4,因为首次render之后,只能通过useState返回的dispatch修改对应Hook的memoizedState,因此必须要保证Hooks的顺序不变,所以不能在分支调用Hooks,只有在顶层调用才能保证各个Hooks的执行顺序!
useState hook更新过程

function App() {
  const [n1, setN1] = useState(1);
  const [n2, setN2] = useState(2);
  const [n3, setN3] = useState(3);

  useEffect(() => {
    setN1(10);
    setN1(100);
  }, []);

  return (<button onClick={() => setN2(20)}>click</button>);
}

pic2
合并了setState

@HCLacids HCLacids added the React label Feb 25, 2022
@HCLacids
Copy link
Owner Author

useEffect

useEffect和useLayoutEffect的区别
useEffect callback是在组件被渲染为真实DOM后执行(所以可以用于DOM操作
useLayoutEffect callback是在组件被渲染为真实DOM前执行
deps参数很重要
seEffect可以接受第二个参数deps,用于在re-render时判断是否重新执行callback,所以deps必须要按照实际依赖传入,不能少传也不要多传!
deps数组项必须是mutable的,比如不能也不必传useRef、dispatch等进去
deps的比较其实是浅比较(参阅源码),传入对象、函数进去是无意义

@HCLacids
Copy link
Owner Author

HCLacids commented Feb 25, 2022

useContext

  • 使用React.createContext API创建Context,由于支持在组件外部调用,因此可以实现状态共享

  • 使用Context.Provider API在上层组件挂载状态

  • 使用Context.Consumer API为具体的组件提供状态或者通过contextType属性指定组件对Context的引用

@HCLacids
Copy link
Owner Author

useReducer

作用:用于管理复杂的数据结构(useState一般用于管理扁平结构的状态),基本实现了redux的核心功能,事实上,基于Hooks Api可以很容易地实现一个useReducer Hook:

@HCLacids
Copy link
Owner Author

useCallback

useEffect deps需要准确的传入参数,而由于浅比较,函数、对象每一次都不会相同,所以useCallbacl的出现是为了减少由于函数而导致的不必要的渲染和执行。
解决方案:

  • 将函数移到组件外部(缺点是无法读取组件的状态了)

  • 条件允许的话,把函数体移到useEffect内部

  • 如果函数的调用不止是useEffect内部(如需要传递给子组件),可以使用useCallback API包裹函数,useCallback的本质是对函数进行依赖分析,依赖变更时才重新执行

@HCLacids
Copy link
Owner Author

useMemo & memo

useCallback(fn, deps) === useMemo(() => fn, deps)

在函数组件中,React提供了一个和类组件中和PureComponent相同功能的API React.memo,会在自身re-render时,对每一个 props 项进行浅对比,如果引用没有变化,就不会触发重渲染。

@HCLacids
Copy link
Owner Author

useRef

关于useRef其实官方文档已经说得很详细了,useRef Hook返回一个ref对象的可变引用,但useRef的用途比ref更广泛,它可以存储任意javascript值而不仅仅是DOM引用。

@HCLacids
Copy link
Owner Author

HCLacids commented Feb 28, 2022

@HCLacids
Copy link
Owner Author

let memoizedState = []; // hooks 存放在这个数组 re-render不会重置
let cursor = 0; // 当前 memoizedState 下标 re-render会重置

function useState(initialValue) {
  memoizedState[cursor] = memoizedState[cursor] || initialValue;
  const currentCursor = cursor;
  function setState(newState) {
    memoizedState[currentCursor] = newState;
    console.log(currentCursor)
    // render();
  }
  return [memoizedState[cursor++], setState]; // 返回当前 state,并把 cursor 加 1
}

function useEffect(callback, depArray) {
  const hasNoDeps = !depArray;
  const deps = memoizedState[cursor];
  const hasChangedDeps = deps
    ? !depArray.every((el, i) => el === deps[i])
    : true;
  if (hasNoDeps || hasChangedDeps) {
    callback();
    memoizedState[cursor] = depArray;
  }
  cursor++;
}

let [_,setState] = useState(1);
useEffect(()=>{console.log(cursor)});
setState()

// 每次re-render都会重新按顺序执行,但是memerizedState不会重置,cursor会重置

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant