# Hook

何为“钩子”,在函数式组件中,钩入 React 的功能特性。官方的解释就是,它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性

# useState

让函数式组件拥有自己的 state,函数式组件,一般来说在函数退出后变量就会消失,使用 useState ,变量将被 React 保留

const [value, setValue] = React.useState(0);

useState 可以传入一个函数, 该函数只会在初始化的时候调用一次。否则会每次渲染的时候都会执行里面的参数(参数本身是在调用函数)

setValue 可以传入一个具体的值, 如果需要使用旧数据,则需要传入一个函数 value => value + 1

# useEffect

什么叫“副作用”?

首先解释 纯函数(Pure function):给一个 function 相同的参数,永远会返回相同的值,并且没有副作用;这个概念拿到 React 中,就是给一个 Pure component相同的 props, 永远渲染出相同的视图,并且没有其他的副作用;纯组件的好处是,容易监测数据变化、容易测试、提高渲染性能等;

副作用(Side Effect)是指一个 function 做了和本身运算返回值无关的事,比如:修改了全局变量、修改了传入的参数、甚至是 console.log(),所以 ajax操作,修改 dom都是算作副作用的;

是每一次渲染后调用,而不是值改变时调用,而且每次传递给 useEffect 的函数也是不一样的

# useContext

接受一个 context 对象,当上层最近的 provider 更新时,该 hook 会触发组件重新渲染

const value = useContext(MyContext);

# useReducer

const [state, dispatch] = useReducer(reducer, initialArg, init);

useState 的代替方案,当 state 的逻辑比较复杂切包含多个子值,或者下一个 state 依赖于之前的 state,甚至子组件需要修改父组件的值(传递 dispatch 给子组件)。

疑问? 需要保持 dispatch 的命名吗? 如果有多个 useReducer 需要怎么处理?

# useMemo

有点类似于 vue 中的计算属性,需要指定依赖项数组,当依赖项不变时,会使用缓存中的值,当依赖发生变化时,会重新计算结果。在函数式组件中,建议使用作为性能优化的手段。

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

# useCallback

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

可以将返回结构传递给子组件,在子组件中使用 useEffect 判断 useCallback 的返回结果是否发生变化来变更自己的 state,减少渲染。

const Child = ({ cb }) => {
  const [count, setCount] = useState(0);
  useEffect(() => {
    setCount(cb());
  }, [cb]);
  return <div>{count}</div>;
};

const Parent = () => {
  const [count, setCount] = useState(0);
  const cb = useCallback(() => {
    return count;
  }, [count]);
  return <Child cb={cb} />;
};

注意 shouldComponentUpdate

# useRef

const refContainer = useRef(initialValue);

useRef 返回一个可变的 ref 对象, 其 .current 属性被初始化为传入的参数。

useRef 的一个非常有用的特性

  • useRef 会在每次渲染时,返回同一个 ref 对象
  • useRef 变更 .current 的值,不会触发组件的重新渲染

是不是有点意思了? 类似于 class 中的变量,可以在整个实例中保持

# useImperativeHandle

通过 ref 将自定义的值或方法暴露给父组件, 要和 forwardRef 一起使用

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

# useLayoutEffect

与 useEffect 相似,但会在所有的 DOM 变更后同步调用 effect。可以读取 DOM 布局并同步触发重渲染。可能会阻塞视觉更新

useLayoutEffect 与 componentDidMount、componentDidUpdate 的调用阶段是一样的

# useDebugValue

用于显示自定义 hook 的标签