Skip to content
New issue

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

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

Already on GitHub? # 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会重置

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

No branches or pull requests

1 participant