React 16.7 react hooks学习/踩坑笔记
发布于 6 年前 作者 dislido 3643 次浏览 来自 分享

最近在研究react 16.7的react hooks功能,写个笔记分享一下,还在观望的同学们可以参考一下
基本上是对官网内容的搬运、翻译、补充,加上自己的实践内容
https://reactjs.org/docs/hooks-intro.html

先说感想:最大的改变就是将代码从按生命周期分组变成了按功能分组,以前一个功能的代码经常要分散在didMount,didUpdate,willUnmount这些生命周期里写,每个生命周期里不同的功能代码混杂在了一起 而现在可以把功能A的代码放在一起写在同一个地方,功能B代码放在一起写在另一个地方
还有一个重大的意义是把函数式组件的生命周期功能补全了,现在class组件能做的功能函数组件基本都能做了(还有少数功能不能,比如componentDidCatch,但是会在以后的版本中提供此功能,最终函数式组件会覆盖类组件的所有功能),而且代码量会更少,可读性更高

不过把旧的class组件用function形式重写的工作量还是挺大的,幸好可以一个个慢慢改

现在react 16.7还在alpha版本,可以用npm i react@next react-dom@next更新到此版本

基本用法

首先react hooks只能用在函数式组件中,也就是用function方式写的组件,不能用在class组件中
其次react hooks只能写在在函数组件的最顶层域中,不能写在循环或嵌套的函数内
但是有一个例外,见下文的自定义hook

state hook: useState

直接把官网的例子贴过来讲解,一个简单的点击+1功能

import React from 'react';

const { useState } = React;

function Example() {
  // Declare a new state variable, which we'll call "count"
  // useState以数组形式返回两个值,[0]是state值,[1]是对应该值的setState函数
  // 本例只声明了一个state,如果你需要多个state,那就使用多次useState
  const [count, setCount] = useState(0);

  // 直接使用count值,用setCount来更新count值(忘掉this.state吧)
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

把一个this.state和this.setState变成了多个state和对应的setState函数了

effect hook: useEffect

这个hook算是componentDidMount,componentDidUpdate,componentWillUnmount功能的集合,它会在本组件每次render后执行(didMount和didUpdate)
useEffect主要用于处理组件中有副作用(side effects)的功能
还是贴官网例子,对上例增加一个功能,使当前页面的标题也随着点击改变

import React from 'react';

const { useEffect } = React;

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

  // 在class方式的组件中,我们需要在componentDidMount和componentDidUpdate两个函数中写同样的代码
  // 现在,只需要在useEffect中写一遍就可以了
  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;

    // 如果想在组件被卸载的时候将标题恢复,那么可以返回一个函数(对应componentWillUnmount)来处理
    // return () => document.title = 'old title';
  });

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

还有其他的用法:一个组件在挂载时需要addListener,卸载时需要removeListener,以前的做法时分别在componentDidMountcomponentWillUnmount分别写,现在可以用useEffect写在一起

useEffect(() => {
  document.addEventListener(type, listener);

  return () => {
    document.removeEventListener(type, listener);
  };
});

例:实现onChange:

每次组件内的值改变,就调用一次onChange

function Example({ onChange }) {
  const [a, setA] = useState(0);
  const [b, setB] = useState(0);

  useEffect(() => {
    onChange(a, b);
  }, [a, b]);

  return (
    <>
      a:<Input value={a} onChange={setA} />
      b:<Input value={b} onChange={setB} />
    </>
  );
}

这里要使用到useEffect的第二个参数,这是一个数组,功能类似于shouldComponentUpdate,如果组件的两次更新之间这个数组内的值没有发生任何变化(使用===逐个判断),那么这次更新就不会调用这个useEffect的函数,用于性能优化,在绝大多数情况下建议传入这个参数,否则每次render都会调用一次,在本例这样的情况中还会导致死循环的出现(onChange导致父组件更新->父组件更新导致本组件更新->本组件更新导致onChange触发,我重构第一个组件时就踩了这个坑)

context hook: useContext

这个hook很简单:
const context = useContext(Context);
其中,Context参数是React.createContext的返回值
然后正常使用

其他hook

  • useReducer
  • useCallback
  • useMemo
  • useRef
  • useImperativeMethods
  • useMutationEffect
  • useLayoutEffect

自定义hook

这是hook唯一可以写在component函数之外的地方
写个简单的例子

function useNumberInput(init) {
  const [value, setValue] = useState(init);
  const onInput = e => {
    const val = +e.target.value;
    if (Number.isNaN(val)) return;
    setValue(val);
  }
  return { value, onInput };
}

function App() {
  const foo = useNumberInput(123);
  return (
    <>
      <div><input {...foo} /></div>
      <p>value={foo.value}</p>
      <p>typeof value={typeof foo.value}</p>
    </>
  );
}

用非常简单的方式实现了一个只能输数字的输入框,并且输入的内容会自动转换为Number类型,而且useNumberInput可复用

回到顶部