1. 前言

https://overreacted.io/zh-hans/a-complete-guide-to-useeffect/
React Hooks React 16.8 引入的新特性,允许我们在不使用 Class 的前提下使用 state 和其他特性。React Hooks 要解决的问题是状态共享,是继 render-props higher-order components 之后的第三种状态逻辑复用方案,不会产生 JSX 嵌套地狱问题。
React Hooks 的设计目的,就是加强版函数组件,完全不使用”类”,就能写出一个全功能的组件。
Hook 这个单词的意思是”钩子“。 React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码”钩”进来。 React Hooks 就是那些钩子。

2. useState

2.1 useState介绍

useState用于为函数组件声明状态变量(state), 纯函数不能有状态, 所以把它放在钩子里面。

  1. const [count, setCount] = useState(0)

该函数接收的参数是状态的初始值(Inital State), 上例的初始值就是0。 该函数返回一个数组, 数组的第一个元素式一个变量(count), 指向状态的当前值, 第二个元素式一个函数, 用来更新状态, 约定式set前缀加上状态的变量名(setCount

2.2. setXxx()的2种写法:

  • setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值

    1. import React, { useState } from 'react'
    2. export default function State(){
    3. const [count, setCount] = useState(0)
    4. const add = () => {
    5. setCount(count+1)
    6. }
    7. return <div>
    8. <h2>当前的值: {count}</h2>
    9. <button onClick={add}>加1</button>
    10. </div>
    11. }
  • setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值

    1. import React, { useState } from 'react'
    2. export default function State(){
    3. const [count, setCount] = useState(0)
    4. const add= () => {
    5. setName( count=> count+ 1)
    6. }
    7. return <div>
    8. <h2>当前的值: {count}</h2>
    9. <button onClick={add}>加1</button>
    10. </div>
    11. }

    2.3 不能使用判断语句

    ```javascript import React, { useState } from ‘react’;

let showSex = true function Example2(){ const [ age , setAge ] = useState(18) if(showSex){ const [ age , setAge ] = useState(18) showSex=false } return (

张三今年:{age}岁

) } export default Example2;

  1. **useState**不能在**if...else...**这样的条件中进行调用, 必须按照相同的顺序进行渲染。
  2. <a name="bVdkh"></a>
  3. ## 2.4 useState和setState的区别:
  4. **不同点:**
  5. - **useState** 初始**state**的时候可以是**任意的数据类型**,**setState**初始**state**的时候**state**只能是**对象**;
  6. - **setState**是对状态的**合并**, 但是**useState**是对状态进行**替换**;只不过内部有个**防抖**的优化才导致组件不会立即被重新渲染
  7. - 使用**useState**改变状态时, 使用**Object.is**算法比较状态的前后值,如果前后状态相同, 那么组件不会重新渲染。
  8. ```javascript
  9. const [count, setCount] = useState(0);
  10. function update(){
  11. setCount(0) // 不会重新渲染
  12. }
  13. //
  14. function update(){
  15. setCount() // 会重新渲染
  16. }
  • 但是class组件的setState,即使状态的前后时相同的,也会进行渲染。

    1. state = {
    2. count:0
    3. }
    4. update = () => {
    5. this.setState({
    6. count:0 //会渲染
    7. })
    8. }
    9. update = () => {
    10. this.setState({}) //会渲染
    11. }

    相同点:

  • setStateuseState拿到最新的state后,重新渲染页面,页面展示最新的数据状态。

总结React Hooks不能出现在条件判断语句中, 因为它必须有完全一样的渲染顺序。

3. useEffect()

3.1 useEffect的介绍

Effect Hook可以让你在函数组件中执行副作用操作,最常见的就是向服务器请求数据。以前放在componnetDidMount里面的代码,现在可以放在useEffect()
当你调用 useEffect 时,就是在告诉 React 在完成对 DOM 的更改后运行你的“副作用”函数
useEffect的用法如下:

  1. useEffect(() => {
  2. // Async Action
  3. }, [dependencies])

可以把useEffect Hook看作是componentDidMountcomponentDidUpdatecomponentWillUnmount这三个函数的组合。
useEffect本身是一个函数,由React框架提供,在函数组件内部调用即可。

  1. import React, { useEffect } from 'react';
  2. function Welcome(props) {
  3. useEffect(() => {
  4. document.title = '加载完成';
  5. });
  6. return <h1>Hello, {props.name}</h1>;
  7. }

useEffect的第一个参数是一个函数,它就是所要完成的副效应组件加载以后,React就会执行这个函数。

3.2 useEffect到底做了什么?

通过使用useEffect, 可以告诉组件需要在渲染后执行哪些操作。 React会保存传递的函数, 并且在执行DOM更新在调用它。

3.3 为什么在组件内部调用useEffect?

useEffect放在组件内部,让我们可以在effect中直接访问state变量, 不需要特殊的API来读取它, 因为它已经保存在函数作用域中了。

3.4 useEffect会在每次渲染后都执行吗?

默认情况下, 它在第一次渲染之后或者每次更新之后都会执行。
3.6 为什么要在effect中返回一个函数
这个effect有可选的清除机制。 每个effect都可以返回一个清除函数, 可以将添加的定时器订阅的逻辑放在一起,这个新的函数将会在组件下一次重新渲染之后执行。

  1. useEffect(() => {
  2. const timer = setTimeout(() => {
  3. setCount(count+1)
  4. console.log(123);
  5. },1000)
  6. return () => {
  7. clearInterval(timer)
  8. }
  9. },[name])

3.7 useEffect的第二个参数

3.7.1 第二个参数为[]

  1. 如果依赖数组为空数组,那么回调函数会在第一次渲染结束后(componentDidMount)执行,返回的函数会在组件卸载时(componentWillUnmount)执行。
    1. useEffect(()=>{
    2. const users = 获取全国人民的信息()
    3. },[])
    3.7.2 第二个参数为空
    如果不传依赖数组,那么回调函数会在每一次渲染结束后(componentDidMountcomponentDidUpdate)执行。
    1. useEffect(()=>{
    2. const users = 获取全国人民的信息()
    3. })
    3.7.3 第二个参数为[name]
    useEffect最后加[],并且[]里面加的字段就表示,这个字段更改了,我这个effect才执行
    1. useEffect(() => {
    2. console.log('我变了');
    3. },[count])
    3.7.4 第二个参数为[name, age]
    如果我想要分别name和age呢:可以多写几个
    1. useEffect(() => {
    2. console.log('我变了');
    3. },[count,age])
    3.7.5 取消订阅
    如果我们之前订阅了什么,最后在willUnMount这个生命周期里面要取消订阅,在effectreturn里面可以做取消订阅的
    1. useEffect(() => {
    2. const timer = setTimeout(() => {
    3. setCount(count+1)
    4. console.log(123);
    5. },1000)
    6. return () => {
    7. clearInterval(timer)
    8. }
    9. },[name])

    3.8 useEffect()的用途:

  • 获取数据(data fetching)
  • 事件监听或订阅
  • 改变DOM
  • 输出日志

    3.9 useEffect中如何使用async await

    ```javascript //1. 使用自执行函数 useEffect(()=>{ // 使用自执行函数 IIFE (async function fn(){
    1. await otherFn();
    })()
    },[])

//2. 在useEffect的回调参数内部定义一个async函数: useEffect(()=>{ const fn=async ()=>{ // do something await otherFn() } fn() },[])

// 3. 在useEffect外部定义async函数,在回调参数中去执行: const fn=async ()=>{ // do something await otherFn() } useEffect(()=>{ fn() },[])

  1. <a name="I0cCl"></a>
  2. # 4. useLayoutEffect
  3. ```javascript
  4. import React,{useState, useEffect, useLayoutEffect, useRef}from 'react'
  5. export default function App() {
  6. const [n, setN] = useState(0)
  7. const time = useRef(null)
  8. const onClick = () => {
  9. setN(i => i + 1)
  10. time.current = performance.now()
  11. }
  12. useEffect(() => {
  13. if(time.current){
  14. console.log('useEffect时序')
  15. console.log(performance.now()-time.current)
  16. }
  17. })
  18. useLayoutEffect(() => {
  19. if(time.current){
  20. console.log('useLayoutEffect时序')
  21. console.log(performance.now()-time.current)
  22. }
  23. })
  24. return (
  25. <div className="App">
  26. n:{n}
  27. <button onClick={onClick}>+1</button>
  28. </div>
  29. );
  30. }

5. useRef()

useRef是一个对象,它有一个current属性, 不管函数组件执行多少次, 而useRef()返回的永远都是原来的那个。
(1). Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
(2). 语法: const refContainer = useRef()
(3). 作用:保存标签对象,功能与React.createRef()一样

  1. const myRef = useRef();
  2. const show = () => {
  3. console.log(myRef.current.value);
  4. }
  5. <input type="text" placeholder='请输入值' ref={myRef} />
  6. <button onClick={show}>显示输入框内的值</button>

5.1 useRef的特点:

  1. useRef是一个只能在函数组件使用的方法
    2. useRef是除字符串ref函数refcreateRef之外的第四种获取ref的方法
    3. useRef在渲染周期内永远不变, 因此可以用来引用某些数据
    4. 修改ref.current不会引发组件重新渲染

    5.2 useRef VScreateRef

  2. 两者都是获取ref的方式, 都有一个current属性
    2. useRef只能用于函数组件createRef可以用于类组件中
    3. useRef在每次重新渲染后都保持不变, 而createRef每次都会发生变化

    6. useContext ——共享状态钩子

    useContext用于接收一个context对象并返回该context的值, 可以实现跨层级的数据共享。 ```javascript import React, { useState, useContext } from ‘react’

const CountContext = React.createContext() export default function App() { const [count, setCount] = useState(10) function updateCount() { setCount(count + 5) } return (

我是APP组件

当前的count为:{count}


) }

function Parent() { return (

我是Parent组件

) } function Child() { const count = useContext(CountContext) return (

我时Child组件

父组件传过来的值:{count}

) }

```