React Hook学习(索引)

Hook是什么?有哪些?

Featured image

介绍

Hook是React 16.8的新特性,它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

基础的Hook:

  1. useState
  2. useEffect
  3. useContext

额外的Hook:

有些是上面基础 Hook 的变体,有些则仅在特殊情况下会用到。

  1. useReducer
  2. useCallback和useMemo
  3. useRef
  4. useImperativeHandle
  5. useLayoutEffect
  6. useDebugValue

自定义Hook

Hook能解决什么问题?

React 16 已经是 Javascript 圈子里非常受欢迎,且独特的框架,有这些优点:

通过React.Component定义本地组件

class CountComponent extends React.Component {
  constructor (props) {
    super(props)

    this.state = {
      count: 0
    }

    this.updateCount = this.updateCount.bind(this)
  }

  componentDidMount () {
    this.updateCount(this.props.num)
  }

  componentDidUpdate (prevProps) {
    if (prevProps.num !== this.props.num) {
      this.updateCount(this.props.num)
    }
  }

  updateCount (num) {
    this.setState({ count: num })
  }

  clickAdd () {
  	this.updateCount(this.state.count + 1)
  }

  render() {
    return (
      <ul>
        <li><a>{this.state.count}</a></li>
        <input type="button" value="Add" onclick="clickAdd()" />
      </ul>
    )
  }
}

我们遇到了哪些问题:

  1. 具有状态的组件必须用类实现,比较耦合
  2. 组件支持函数来创建,但是支持力度相当有限
  3. 总是要调用super(props)
  4. 有各种this围绕着
  5. 逻辑的共享没有合适的手段

Hook的出现很好的解决了这些问题, 而且远不止这些。

规则

Hook 是向下兼容的。 Hook的本质就是JavaScript函数, 使用它要遵循两条规则:

1.只在最顶层使用Hook

不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层调用他们。

遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确。

2.只在React函数中调用Hook

不要在普通的 JavaScript 函数中调用 Hook。

闭包陷阱

当我们更新状态的时候,React会重新渲染组件。每一次渲染都能拿到独立的count 状态,这个状态值是函数中的一个常量。每一次调用引起的渲染中,它包含的count值独立于其他渲染

import React, { useState } from 'react';

function Example() {
	const [count, setCount] = useState(0);

    function alertCount(){
        setTimeout(()=>{
          // alert 只能获取到点击按钮时的那个状态
          alert(count);
        }, 3000);
      }

	return (
	  <div>
	    <p>You clicked {count} times</p>
	    <button onClick={() => setCount(count + 1)}>Click me</button>
        <button onClick={alertCount}>alertCount</button>
	  </div>
	);
}

export default Example;

运行一下,先点击1次「alertcount」, 再多点3次「Click me」, 发现 页面显示的是You clicked 3 times, 但是alert里显示的是0,和我们预想的不一样。
Hook都有闭包陷阱问题。

原因是:
每一次 render 都会生成一个闭包,每个闭包都有自己的 state 和 props。 所以在异步函数中访问hooks的state值拿到的是当前的闭包值,并不是最新的state值。

解决闭包陷阱的办法:

1.添加count依赖, 缺点是会重复生成和销毁定时器,影响性能

useEffect(() => {
    ...
    // 添加count依赖
  }, [count]);

2.setCount里换成箭头函数

// set函数可以为一个函数(类似于setState) 参数为上一次的state值
setCount(count => count + 1);

3.使用useReducer

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'add':
      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: 'add'})}>+</button>
    </>
  );
}

4.使用useRef

初始化的 useRef 执行之后,返回的都是同一个对象。比如:

ref = useRef()

组件每一次渲染的过程中,返回的都是同一个对象,每次组件更新所生成的ref指向的都是同一片内存空间,所以每次都能拿到最新的值。

其他库对Hook的支持

React Redux 从 v7.1.0 开始支持 Hook API 并暴露了 useDispatch 和 useSelector 等 hook。
React Router 从 v5.1 开始支持 hook。
其它第三库也将即将支持 hook。

最新版的 Flow 和 TypeScript React 定义已经包含了对 React Hook 的支持。