# React hooks 手记

记录 react hooks 相关要点。

# useState

  • setState 可以传入一个函数来更新值,函数的入参数包含旧的 state 值
setState(prevState => {
// 也可以使用 Object.assign
return {...prevState, ...updatedValues};
});
  • useState 不会自动合并更新对象,不过可以使用函数式的方式来实现,同上
  • 调用 State Hook 的更新函数并传入当前的 state 时,React 将跳过子组件的渲染及 effect 的执行。(React 使用 Object.is 比较算法 来比较 state。)
  • initialState 参数只会在组件的初始渲染中起作用,后续渲染时会被忽略。如果初始 state 需要通过复杂计算获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染(只会在第一个渲染时执行)时被调用。如下所示:
const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props);
  return initialState;
});

# useEffect

  • 每次函数渲染都会调用

  • 同 vue 中的 watch 类似功能

  • 可以衍生出初始化时是否调用处理cb

  • 虽然 useEffect 会在浏览器绘制后延迟执行,但会保证在任何新的渲染前执行。React 将在组件更新前刷新上一轮渲染的 effect

# useReducer

使用方式如下:

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

# useCallback

  • 生成一个函数,该函数能确保函数里面的依赖state都是最新的值。然后就可以在适当的业务中去调用这个函数。
  • 把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本(函数内部缓存了最新的state依赖值),该回调函数仅在某个依赖项改变时才会更新。
  • 当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子组件时,它将非常有用。
  • useCallback(fn, deps) 相当于 useMemo(() => fn, deps)。只不过useCallback返回的是一个缓存函数

# useMemo

  • 返回一个计算的值,且只会在依赖值变化后去更新返回的值,避免每次都去计算。

# useRef

  • 返回一个可变对象,在生命周期内保持不变。.current 保存目标对象值

# useImperativeHandle

useImperativeHandle 可以定义在父组件使用 ref 引用当前组件的 ref 值,通常和 forwardRef 配合使用,因为 forwardRef 会把 ref 值从属性值转发出来,作为函数式组件的第二个参数传入进来,便于 useImperativeHandle 使用,自定义 ref。

function FancyInput(props, ref) {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));
  return <input ref={inputRef}  />;
}
FancyInput = forwardRef(FancyInput);

# forwardRef

React.forwardRef 会创建一个React组件,这个组件能够将其接受的 ref 属性转发到其组件树下的另一个组件中。一般情况不会做这样的操作,但在以下两种场景中特别有用:

  • 转发 refs 到 DOM 组件
  • 在高阶组件中转发 refs

React.forwardRef 接受渲染函数作为参数。React 将使用 props 和 ref 作为参数来调用此函数。此函数应返回 React 节点。

const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
));

// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;

更多参考 (opens new window)

# useLayoutEffect

  • 其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。

  • useLayoutEffect 会在 dom 渲染完成之后才会调用(可能会阻塞UI渲染),useEffect 则是在 dom 渲染之前。

# 可自定义钩子类型

useUpdate: 第一次不执行,之后执行(useEffect)

其他的一些自定义钩子可以参考aHooks (opens new window)的实现思路,按需自己实现