09- React Hooks初识

149 阅读4分钟

一、Hooks是什么

  • Hooks是React v16.8中的新增功能。

  • 抛开React赋予的概念 hooks就是一些普通的函数

赋予的: 为函数组件提供状态、生命周期等原本class组件提供的React功能

作用: 为函数组件提供状态、生命周期等原本class组件提供的React功能。

注意: Hooks只能在函数组件中使用,自此,函数组件成为React的新宠儿。

  • React v16.8以前:class组件( 提供状态 ) + 函数组件( 展示内容 )
  • React v16.8及其以后:
    1. class 组件(提供状态) + 函数组件(展示内容)
    2. Hooks( 提供状态 ) + 函数组件 ( 展示内容 )

注意: Hooks以后,不能再把函数组件成为无状态组件了,因为Hooks为函数组件提供了状态

二、为什么要有Hooks

从以下两个角度来看Hooks出现之前 React存在的问题

两个角度:1 组件的状态逻辑复用 2 class组件自身的问题

1. 组件的状态逻辑复用:

  • 在Hooks之前,组件的状态逻辑复用经历了:mixins(混入)、HOCs(高阶组件)、render-props等模式。
  • mixins的问题:1 数据来源不清晰 2 命名冲突
  • HOCs、render-props的问题:重构组件结构,导致组件形成JSX嵌套地狱问题。

2. class组件自身的问题

  • 选择:函数组件和class组件之间的区别以及使用那种组件更合适
  • 需要理解class中的this是如何工作的。
  • 互相关联且需要对照修改的代码被拆分到不同的生命周期函数中
  • 相比于函数组件来说,不利于代码压缩和优化,也不利于TS的类型推倒

三、React基础

1、概述

Reactt内置Hooks有很多个,可以分为以下两类

  1. 基础Hooks(最常用
    • useState 、useEffect
  2. 其他Hooks
    • useRef、useContenxt、useReducer、useCallback、useMemo

2、useState

作用:为函数组件提供状态

// 1、创建
const [count,setCount] = useState(0)
// 2、使用
setCount((currentCount) => currentCount+1)   // 可以确定获取的是当前值
setCount(count+1)   												 // setCount是异步的

执行过程

  • 组件第一次渲染:

    1. 从头开始执行该组件中的代码逻辑。
    2. 调用useState(0) 将传入的参数作为状态初始值
    3. 渲染组件,此时得到的是0
  • 组件第二次渲染:

    1. 执行setCount() 修改状态,因为状态发生改变,所以重新渲染
    2. 组件重新渲染时,会再次执行该组件中的代码逻辑。
    3. 再次调用useState(0),此时内部会拿到修改后的值
    4. 再次渲染组件,获取到的是count值为:1

3、useEffect

作用:处理函数组件中的副作用

副作用:不是这个函数组件主要作用的额外作用

对于React组件来说 主作用就是根据数据渲染UI 除此之外就是副作用(比如,修改DOM)

常见的副作用:

  • 数据(Ajax)请求、手动修改DOM
  • console.log 、修改全局变量、修改函数引用类型的参数 ...

官方解释

在计算机科学中,如果一个函数或其他操作修改了其局部环境之外的状态变量值,那么它就被称为有副作用

1、useEffect第二个参数不为空数组时

import {useState,useEffect} from "react"
import ReactDOM from "react-dom"

const Counter = () => {
  // 计数器
  const [count,setCount] = useState(0)
  // 完成状态
  const [status,setStatus] = useState(false)
  
  
  // 只有count变化时才需要重新执行
	useEffect(()=>{
  	document.title = `当前已点击${ count }次`
	},[ count ])
  
  const onClick = () => {
    setCount(count+1)
  }
  
  const onFinish = () => {
    setStatus(!status)
  }
  
  return (
  	<div>
    	<h1>{count}</h1>
      <button onClick={onClick}>+1</button>
      <button onClick={onFinish}>+1</button>
    </div>
  )
}

ReactDOM.render(<Counter />, document.getElementById('root'))

- 第一个参数:回调函数,就是在该函数中写副作用代码。
- 第二个参数:数组,数组中的元素成为依赖项 只有count变化时才会执行回调函数
- 执行时机:该effect,会在每次组件更新(DOM更新)后执行。
- 执行时机:该effect的返回值,effect重新执行前执行

1、useEffect第二个参数为空数组时

import {useState,useEffect} from "react"
import ReactDOM from "react-dom"

const Counter = () => {
  
  // 再次调用useEffect 来实现window绑定 []只会执行一次
  useEffect(() => {
    const handleMove = () => console.log('鼠标移动了');
    window.addEventListener('mousemove',handleMove);
  // 卸载时解绑事件 用到effect的返回值
    return () => window.removeEventListener('mousemove',handleMove)
  },[])
  
  return (
  	<div> </div>
  )
}

ReactDOM.render(<Counter />, document.getElementById('root'))

- 第一个参数:回调函数,就是在该函数中写副作用代码。
- 第二个参数:空数组,如果是空数组组件只会执行第一次渲染执行effect 相当于componentDidMount
- 执行时机:该effect,会在每次组件更新(DOM更新)后执行。
- 执行时机:该effect的返回值,只会在组件卸载时执行一次 相当于componentDidUnmount

2、使用useEffect Hook 发送请求获取数据

  • effect 只能是一个同步函数
  • 因为 effect的返回值应该是一个清理函数,卸载时或者依赖项变化时执行。
  • 不能延迟执行 所以不能是promise
const axios = new Promise((resolve,reject) => {
  setTimeout(()=>resolve({count:10}),1000)
})
import {useState,useEffect} from "react"
import ReactDOM from "react-dom"

const Counter = () => {
  // 计数器
  const [count,setCount] = useState(0)
  
	useEffect(() => {
  		const loadCount = async () => {
    	const {count} = await axios()
    	setCount(count)
  	}
  	loadCount()
	},[])
   
  return (
  	<div>
    	<h1>{count}</h1>
    </div>
  )
}

ReactDOM.render(<Counter />, document.getElementById('root'))