学习React Hooks简单记录一下官方文档关于Hooks的一些特征和使用方式,摘录自官方文档。后续使用后,再做总结
Basic React Hooks
useState
| 1
 | const [data, setData] = useState(initData);
 | 
 
useState返回一个数组,数组内第一个元素是state的值,初始值为initState,利用js的结构将该数组解构赋值,如果设置多个state可以多次调用useState,但是useState也可以传入一个数组,数组内的项为多个initState,是否可以声明多个state的时候使用一个useState有待研究,后续看下项目
useEffect
用于写有副作用的代码,可以实现生命周期中的componentDidMount,componentUpdate,componentWillUnmount。
| 1
2
3
4
5
6
 | useEffect(() => {
    effect code...
    return () => {
        cleanup code
    }
})
 | 
 
useEffect可以添加回调,在effect code执行完成后执行,在useEffect中的代码React每次渲染,都会执行,这也意味着性能的损耗(无论是否真的需要执行,state跟之前是否一样),在class组件中可以使用componentDidUpdate:
| 1
2
3
4
 | componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
    document.title = `You clicked ${this.state.count} times`;
}
 | 
 
在hooks中可以使用useEffect的第二个参数
| 1
2
3
 | useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes
 | 
 
如果使用该优化,确保数组包含了来自组件的所有随着时间会变化的value,但是这样实现的是类似于componentDidUpdate,如果想实现componentDidMount和componentWillUnmount,需要给useEffect第二个参数添加空数组[],这相当于告诉React当前useEffect中的代码不依赖任何来自state或props的值,所以不需要re-render。
useContext
| 1
 | const value = useContext(MyContext);
 | 
 
Additional Hooks
useReducer
| 1
 | const [state, dispatch] = useReducer(reducer, initialArg, init);
 | 
 
使用demo
|  1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 | 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({initialState}) {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    </>
  );
}
 | 
 
React可以确保dispatch函数功能的稳定并且在re-render时候不会改变
懒初始化initState
使用useReducer的第三个参数,demo如下
|  1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 | function init(initialCount) {
  return {count: initialCount};
}
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    case 'reset':
      return init(action.payload);
    default:
      throw new Error();
  }
}
function Counter({initialCount}) {
  const [state, dispatch] = useReducer(reducer, initialCount, init);
  return (
    <>
      Count: {state.count}
      <button
        onClick={() => dispatch({type: 'reset', payload: initialCount})}>
        Reset
      </button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    </>
  );
}
 | 
 
useCallback
| 1
2
3
4
5
6
 | const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);
 | 
 
返回记忆化的回调。
该hooks用于实现类似于shouldComponentUpdate的功能。 用于防止不必要的渲染
useCallback(fn, deps)相当于useMemo(() => fn, deps)
useMemo
| 1
 | const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
 | 
 
返回一个记忆化的值。
在计算机科学中,记忆化(英语:memoization而非memorization)是一种提高程序运行速度的优化技术。通过储存大计算量函数的返回值,当这个结果再次被需要时将其从缓存提取,而不用再次计算来节省计算时间。 记忆化是一种典型的时间存储平衡方案。
传递“create”函数和依赖项数组。useMemo只会重新计算那些依赖发生变化的记忆化的值。这种优化避免了每次渲染的昂贵计算。
传递给useMemo的函数在渲染期间运行,所以不能传递带有副作用的函数。
如果没有数组提供,在每次渲染都会计算新值。
useRef
| 1
 | const refContainer = useRef(initialValue);
 | 
 
useRef返回值的.current属性就是给useRef传递个初始value。返回的object将可以在整个生命周期使用。
使用demo
|  1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
 | function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}
 | 
 
useRef在内容变更时不会通知你,.current变化不会导致re-render。
useImperativeHandle
| 1
 | useImperativeHandle(ref, createHandle, [deps])
 | 
 
useImperativeHandle自定义使用ref时公开给父组件的实例值,和通常一样,应该避免使用refs的命令式代码,useImperativeHandle应该使用forwardRef。
|  1
 2
 3
 4
 5
 6
 7
 8
 9
10
 | function FancyInput(props, ref) {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));
  return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
 | 
 
useLayoutEffect
与useEffect相同,但是是在所有的DOM变化完成后同步触发。在浏览器绘制之前,将在useLayoutEffect内部计划的更新将同步刷新,该方法会组织views的更新,但是useEffect是异步的,所以不会阻止。