为什么须要react hooks

从2013开始对外公布react,作为开发者,对于react的开发习惯经验了一些变动,如下:

  1. 最开始的React 提供 React.createClass API 创立 React 组件,【此时class还没有正式进入ecma标准】;

    • 此时开发者须要依照标准去应用createClass API,
  2. React 在 V0.13.0 版本中引入 React.Component API,容许开发者应用原生 JavaScript class 形式创立组件;

    • 此时须要开发者理解 constructor 和 super调用,曾经 this 的绑定;
    • 前面有了试验中 class filed 个性来帮忙咱们跳过 constructor;
    • 高阶组件、render props、生命周期扩散解决等引入;
  3. react hook带来的灵便,给函数组件注入了新的生命力;

更多参考

Hook的定义

Hook 是什么呢?上面是官网给出的定义:

Hooks are functions that let you “hook into” React state and lifecycle features from function components

Hook 实质上是函数,它可能让函数组件也领有状态和生命周期的个性。这意味着咱们本来只能应用 class 组件实现的少数性能,当初用函数组件也能实现。不过 Hook 的应用范畴也有肯定限度,限定于函数组件和自定义 hook 中。

Hooks 名称对立约定以 use 前缀结尾(比方 usexxx),因为 React 还无奈自动识别哪些是一般 JavaScript 函数,哪些是 Hooks 函数。上面咱们将介绍函数组价和 React Hooks 是如何解决 class 组件的痛点的。

useState简介

useState 次要作用就是给函数组件赋予状态(State)。useState 的入参为状态的初始值,会返回两个出参:一个为读取状态的变量,另外一个为写入状态的函数,当然出参的命名是没有任何限度的。从上面示例能够看出,应用函数组件再也不必关怀 super(props) 和 this 指针问题。

import React, { useState } from 'react';function Counter () {  const [count, setCount] = useState(0);  function handleClick() {    setCount(count + 1);  }  return (    <div>      <p>You clicked {count} times</p>      <button onClick={handleClick}>        Click me      </button>    </div>  );}
  1. useState 第一个参数能够是初始值或者是一个返回初始值得初始化函数;
  2. setState 的参数能够是一个新的state值,也能够是返回一个新的state的函数【这个函数的参数就是批改之前的state,这块看起来和class组件中state是类似的】;
  3. setState 这里的setState能够命名为其余的,而且是执行setState将会间接替换原有的state,而不是合并;
  4. setState 可能是异步的过程,所以如果更新state依赖之前的state,请应用函数作为setState的参数来更新state;
  5. useState 是在函数组件或者自定义Hook中应用的,所以无论在mount还是update的时候都会被执行,然而update过程的执行不会从新生成state,而是返回最新的state和setState办法;

留神:state中保留的最好是会影响渲染的数据,如果只是想要一个相似实例数据长久化,那么请应用useRef

useEffect简介

Effect Hook 能够让你在函数组件中执行副作用操作,而数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用

如果你相熟 React class 的生命周期函数,你能够把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。
import React, { useState, useEffect } from 'react';function Example() {  const [count, setCount] = useState(0);  // Similar to componentDidMount and componentDidUpdate:  useEffect(() => {    // Update the document title using the browser API    document.title = `You clicked ${count} times`;  });  return (    <div>      <p>You clicked {count} times</p>      <button onClick={() => setCount(count + 1)}>        Click me      </button>    </div>  );}

useEffect(didUpdate, deps) 该 Hook 接管一个蕴含命令式、且可能有副作用代码的函数。默认状况下,effect 将在每轮渲染完结后执行,但你能够通过第二个参数deps来管制让它 在只有某些值扭转的时候 才执行。

留神点:

  1. 革除Effect:组件卸载时须要革除 effect 创立的诸如订阅或计时器 ID 等资源。要实现这一点,useEffect 函数需返回一个革除函数。然而理论状况中,每次useEffect执行都会革除上一次的effect,而发明新的effect,也就是如果组件屡次渲染(通常如此),则在执行下一个 effect 之前,上一个 effect 就已被革除。所以为了缩小不必要的操作,咱们应该思考缩小useEffect的执行,也就是通过管制useEffect的第二个参数deps;
  2. 传给 useEffect 的函数会提早调用,而且总是在浏览器屏幕实现渲染后提早调用,而不是像class组件的didimount、didiupdate周期一样会合并而后渲染到屏幕中防止中间状态渲染进去【useLayoutEffect Hook 能够达到这种成果】;因而不应在 useEffect 的第一个参数对应的函数中执行阻塞浏览器更新屏幕的操作;
  3. 依赖能够使得useEffect仅仅在某些条件下才会被执行,不过须要确保数组中蕴含了所有内部作用域中会发生变化且在 effect 中应用的变量,否则你的代码会援用到先前渲染中的旧变量,旧变量会保留就得值,而不是最新的值导致呈现bug。而且依赖项数组不会作为参数传给 effect 函数。尽管从概念上来说它体现为:所有 effect 函数中援用的值都应该呈现在依赖项数组中。将来编译器会更加智能,届时主动创立数组将成为可能。

hooks 规定

React Hooks 尽管是函数,但不同一般函数,因而 React Hooks 有两条规定束缚其应用:

  1. 不能在循环语句、条件语句,或者嵌套函数内调用 Hooks。而应该 React 函数顶部调用 Hooks。
  2. 不要在一般的 JavaScript 函数中调用 Hooks,而应该

    • 在 React 函数组件中调用 Hooks
    • 在自定义 Hooks 中调用 Hooks

不过你不必放心下面两条规定,官网提供了 eslint-plugin-react-hooks 插件可能帮你进行代码 eslint 动态查看。

HOOKS 原理

次要是思考闭包:

useState的简略模仿:

import React from 'react';import ReactDOM from 'react-dom';let hooks = []; // 寄存 hooks 的状态let cursor = 0; // 以后 hooks 下标function useState(initialValue) {  hooks[cursor] = hooks[cursor] || initialValue;  const currentCursor = cursor; // “记忆” Hook 的下标,用于后续赋值  function setState(newValue) {    hooks[currentCursor] = newValue;    reRenderComponent();  }  return [hooks[cursor++], setState]; // 下标自增}// 从新渲染function reRenderComponent() {    ReactDOM.render(<App />, document.getElementById('root'));}function App() {  var [count, setCount] = useState(0);  return (    <div>      <div>{count}</div>      <button onClick={() => { setCount(count + 1); }}>        点击      </button>    </div>  );}

useEffect的简略实现:

let hooks = [];let cursor = 0;function useEffect(callback, depArray) {  const hasNoDeps = !depArray; // 没有设置依赖  const deps = hooks[cursor];  const hasChangedDeps = deps    ? !depArray.every((el, i) => el === deps[i])    : true; // 查看依赖是否有发生变化  if (hasNoDeps || hasChangedDeps) {    callback(); // 执行副作用    hooks[cursor] = depArray;  }  cursor++;}