一、Hooks是什么
-
Hooks是React v16.8中的新增功能。
-
抛开React赋予的概念 hooks就是一些普通的函数
赋予的: 为函数组件提供状态、生命周期等原本class组件提供的React功能
作用: 为函数组件提供状态、生命周期等原本class组件提供的React功能。
注意: Hooks只能在函数组件中使用,自此,函数组件成为React的新宠儿。
- React v16.8以前:class组件( 提供状态 ) + 函数组件( 展示内容 )
- React v16.8及其以后:
- class 组件(提供状态) + 函数组件(展示内容)
- 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有很多个,可以分为以下两类
- 基础Hooks(最常用
- useState 、useEffect
- 其他Hooks
- useRef、useContenxt、useReducer、useCallback、useMemo
2、useState
作用:为函数组件提供状态
// 1、创建
const [count,setCount] = useState(0)
// 2、使用
setCount((currentCount) => currentCount+1) // 可以确定获取的是当前值
setCount(count+1) // setCount是异步的
执行过程
-
组件第一次渲染:
- 从头开始执行该组件中的代码逻辑。
- 调用useState(0) 将传入的参数作为状态初始值
- 渲染组件,此时得到的是0
-
组件第二次渲染:
- 执行setCount() 修改状态,因为状态发生改变,所以重新渲染
- 组件重新渲染时,会再次执行该组件中的代码逻辑。
- 再次调用useState(0),此时内部会拿到修改后的值
- 再次渲染组件,获取到的是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'))