背景
在react开发时,函数组件没有setState和生命周期对状态进行管理,这也使得函数组件有了很大的受限,react官方在16.8版本以后引入了hooks,这使得开发者在非class类组件依然可以扩展很多react的特性。除此之外,拥有了hooks,我们可以从生命周期,以及this指向问题解放出来,更多专注业务实现。
Hooks是什么?
简单来说,Hook 就是 JavaScript 函数 他让函数组件拥有生命周期和状态等,原本在Class 组件中才提供的功能。
使用规则:
1.只能在函数最外层调用Hook,不能在循环、条件判断或者子函数中调用
2.只能在React的函数组件中调用,不能在其他JavaScript函数中调用
Hooks前后开发模式对比
react--16.8以前:class组件(提供状态)+函数组件(展示内容)
react--16.8以后:class组件(提供状态)/hooks(提供状态)+(展示内容)
常见的Hooks
1.useState()
示列 const [stateA,setStateA]=useState(0)
stateA:初始化一个状态
setStateA:更改该状态的方法
2.useEffect()
该钩子可以看做是react中componentDidMount,componentDidUpdate和componentWillUnmount这三个生命周期函数的结合,也就是在这三个时候会执行
参数:useEffect()有两个参数,第一个参数是要执行的函数,第二个参数是一个依赖项--数组(可选),根据数组里的变量是否变化决定是否执行函数
常规副作用的几种情况:
如果第二个参数为空,组件的任何更新,该useEffect对应的返回函数和函数都执行;
如果第二个参数为空数组,不监听组件的更新;
如果第二个参数数组中有具体的依赖,对应的依赖数据变化时,函数执行;
在useEffect中返回一个函数,则在组件卸载时,会执行该函数,常用于注销事件,清除定时器等
3.useContext()
1.根组件导入并调用createContext方法,得到Conetext,并导出
import {createContext}from ‘react’
export const Context=createrContext()
2.在根组件中使用Provider组件包裹需要接收数据的后代组件,并通过value属性传递要共享的数据
return (
<Context.Provider value={需要传递数据}>
后代组件
</Provider>
3.需要获取公共数据的后代组件:导入useContext,并按需导入组件Context对象;调用useContext(context)得到value的值
import React,{useContext}from‘react’
import {Context}from‘./index’
const 函数组件=()=>{
const 公共数据=useContext(Context)//即为根组件的value的值
return (组件内容)
}
4.useRef
useRef 用来获取DOM元素对象
import React, {useRef} from "react";
function Ref (){
const box = useRef()
return (
<div>
<div ref={box}>useRef</div>
<button onClick={() => console.log(box)}>+1</button>
</div>
)
}
export default Ref;
保存数据
import React, {useRef, useEffect, useState} from "react";
function Ref (){
let timerId = useRef()
const [count, setCount] = useState(0)
useEffect(() => {
timerId.current = setInterval(() => {
setCount(count => count + 1)
}, 1000)
}, [])
const stop = () => {
console.log(timerId)
clearInterval(timerId.current)
}
return (
<div>
<div>{count}</div>
<button onClick={stop}>停止</button>
</div>
)
}
//这里讲一下 , 为什么不用let一个变量来保存数据, 因为再使用定时器更新状态数据时,
数值的每次变化都会引起组件的更新,每次更新都重新let一个变量,
所以在进行解绑操作的时候,你的let变量为null,它并没有保存定时器,
所以以上场景需要使用useRef进行保存数据,useRef不会因为组件的更新而丢失数据,
虽然组件进行了更新,但是通过useRef保存的数据是不会丢失的,
这里通过useRef中的current来进行保存也是官方要求的写法,
所以如果你想要保存的数据不会因为组件的更新而丢失,就可以使用useRef来保存数据
export default Ref;
5.useMemo
useMemo有两个参数,和useEffect一样,第一个参数是函数,第二个参数是个数组,用来监听某个状态不变化。
useMemo是用来缓存计算属性的,它会在发现依赖未发生改变的情况下返回旧的计算属性值的地址。
useMemo绝不是用的越多越好,缓存这项技术本身也需要成本。
useMemo的使用场景之一是:只需要给拥有巨大计算量的计算属性缓存即可。
useMemo的另一个使用场景是:当有计算属性被传入子组件,并且子组件使用了react.memo进行了缓存的时候,为了避免子组件不必要的渲染时使用
6.useCallback
useCallback第二个参数决定组件更新是否生成新函数,函数内部使用到的依赖尽可能在第二个参数写全,否则会有意想不到的问题
| 何时生成新函数 | 第二个参数 |
|---|---|
| 组件首次执行及更新都会生成新函数 | 空 |
| 组件首次执行生成,之后不变 | [] |
| 组件首次执行、依赖变化时生成新函数 | [state,ref.current] |
useMemo与useCallback比较
useCallback(fn,[m])
相当于
useMemo(()=>fn,[m])
useCallback缓存的是函数,useMemo缓存的是函数的返回结果;
useCallback是用来优化子组件的,防止子组件重复渲染;
useMmemo可以优化当前组件也可以优化子组件,优化当前组件主要是通过memoize来将一些复杂的计算逻辑进行缓存。当然如果只是进行一些简单的计算也没有必要使用useMemo
7.useReducer
useReducer接收两个参数:
第一个参数:reducer函数
第二个参数:初始化的state,返回值为最新的state和dispatch函数(用来触发reducer函数,计算对应的state)
某些场景下,useReducer会比useState更适用,例如state逻辑复杂且包含多个子值,或者下一个state依赖于之前的state等。并且,使用useReducer还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递dispatch而不是回调函数
const [state, dispatch] = useReducer(reducer, init);