为什么用react-hook

在组件之间复用状态逻辑很难

可能你会使用render props或者高阶组件,但一方面,组件结构需要变动,使用render props和高阶组件也会使组件变得很难理解,另一方面会形成嵌套地狱,在用devtools排查问题时,很不方便。

复杂组件变得难以理解

在此之前,我们可能要关注各种生命周期(虽然也写过一篇react生命周期的文章),componentDidMount,componentDidUpdate等等。举自己曾经在实际项目中的两个例子:

  1. componentDidMount里面发了n个请求,componentWillRecieveProps里面做了判断后,又发请求。
  2. componentDidMount添加了addEventListener,componentWillUnmount去removeEventListener。
  3. 如果1和2再混在同一个组件里面

想想,如果不是自己写的代码,去理解比较困难,其次,如果突然你去维护这样的代码,可能一不小心就把分散在其他地方的业务逻辑忘掉了,导致出现bug,就要背锅了。
背锅.jpg

难以理解的 class

不止一次面试中问别人,以及被问过,react组件中constructor内绑定事件,箭头函数,bind绑定事件,都是怎么回事,就巴拉巴拉开始说,constructor只执行一次,箭头函数this是指向上下文的,bind每次都会重新生成函数什么什么的。
再者,可能还要区分什么可以写成无状态组件,负责UI,什么组件要用Component,什么组件要用PureComponent。
hook 使我们从面向生命周期编程,到面向实际业务编程。

2.jpg

异步执行,性能更好

与 componentDidMount 或 componentDidUpdate 不同,使用 useEffect 调度的 effect 不会阻塞浏览器更新屏幕,这让你的 应用看起来响应更快。大多数情况下,effect 不需要同步地执 行。

使用多个 Effect 实现关注点分离

使用 Hook解决 class 中生命周期函数经常包含不相关的逻辑,但又把相关逻辑分离到了几个不同方法中的问题。

Hook是什么?

就是以 use 开头的一系列函数,可以让我们在函数当中使用state其他的react特性。不得不感慨前端更新太快,做前端好难,不过还是要不断学习进步,知晓设计的初衷,知晓怎么用,再知晓其原理,干,就对了。
微信图片_20191213170420.jpg

常用的官方hook API

useState

classstate 一样,useState产生的 state 表示随着时间(用户交互)发生改变,值也会发生改变的量

const RenderFunctionComponent = () {
  const [state, setState] = useState("Hello world!");

  return (
    <Button onClick={() => setState("Hi world!")}>{state}</Button>
  );
}

useReducer

一个useState的替代方案,就像一个简易的redux

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>
    </>
  );
}

useEffect

使用 useEffect 可以用来执行副作用,何为副作用?意思是修改了自我作用域之外的状态,或者除return外,与作用域之外的函数有交互。
在项目中应用广泛,用于初始数据的请求,初始事件的绑定与销毁。中间态的请求(只需要设置useEffect的依赖即可)

  1. 一个 classhook 的官方例子对比
class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
  }

  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}
import { useState, useEffect } from 'react';

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

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
  1. 一个有搜索功能组件的 useEffects
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Input } from "antd";

const RenderFunctionComponent = () => {
  const [searchValue, setSearchValue] = useState();

  useEffect(() => {
    const getList = async () => {
      
    }
    getList();
  }, [searchValue]);

  return (
    <>
      <Input value={searchValue} onChange={e=> setSearchValue(e.target.value)}/>
    </>
  );
}

需注意的是:

  1. useEffect第二个参数如果为空,则只执行一次(事件绑定是一个场景),如果没有第二个参数,则每次渲染均会执行。
  2. 可根据业务的不同划分成多个useEffect,而不用像class组件那样,只能在生命周期中书写,关注业务点分离
  3. 可在 react 内置的 hook api 基础上实现自定义 hook,实现逻辑复用

useContext

获取context提供的值

const value = useContext(MyContext);

useLayoutEffect

在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新
尽可能使用标准的 useEffect 以避免阻塞视觉更新。

useRef

可存储任意变量

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` 指向已挂载到 DOM 上的文本输入元素
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

useCallback

返回一个 memoized 的函数(注意不要滥用:记住函数,以及比较是否需要生成新的函数,未必性能就比串讲一个函数性能好。向下传递回调时可以使用,也可使用useReducer的dispatch向下传递)

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

useMemo

返回一个 memoized 的值(注意不要滥用:在计算开销比较大时,建议采用)

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

结语

有人就说了,官方的 hook 那么少,写代码还要自己封装很多自定义的 hook 。不得不说,世界上大佬那么多,为什么人家那么聪明,还比我们努力。
想了解更多 hook ,请看下篇:

timg.gif


NsNe
1.7k 声望38 粉丝

善良,学习,拼搏。不忘初心,方得始终。