useState-保存状态(惰性初始化)
- 作用
- 函数组件添加状态
- 注意事项
- 初始化以及更新state
- 用来声明状态变量
- 使用步骤(使用useState来创建状态)
- 引入
import React,{useState} from "react"
- 接收一个参数作为初始值
- 返回一个数组,第一个值为状态,第二个为改变状态的函数
- 引入
useEffect-解决副效应
只要是副效应,都可以使用useEffect()
引入。
useEffect,就是执行有副作用的操作,默认情况下,它在第一次渲染之后和每次更新之后都会执行。 可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount
这三个函数的组合。
-
作用/用途
- 给没有生命周期的组件,添加结束渲染的信号
- 获取数据
- 事件监听或订阅
- 改变DOM
- 输出日志
-
注意事项
- 在render之后执行
- 使用
useEffect()
时,如果有多个副效应,应该调用多个useEffect()
,而不应该合并写在一起。
-
使用步骤
- 引入
import React,{useEffect} from "react"
- 接收一个函数作为参数
- 接收第二个参数(依赖列表),只有依赖更新时,才会执行函数
- 返回一个函数。先执行返回函数,再执行参数函数
- 引入
-
示例
import React, { useState, useEffect } from 'react' function EffectFunction() { const [num, setNum] = useState(1) //以下函数先输出1,再输出2 useEffect(() => { console.log("2") document.body.addEventListener('todo', () => { }) return () => { document.body.removeEventListener('todo', () => { }) console.log("1") } }, [num]) return ( <div onClick={() => setNum(num => num + 1)}> 这是一个函数组件-{num} </div> ) } export default EffectFunction
useLayoutEffect-监测DOM
-
作用
- dom更新完成后执行某个操作
-
注意事项
- 有dom操作的副效应hooks
- 在dom更新之后执行
-
使用步骤
- 引入
import React,{useLayoutEffect} from "react"
- 接收一个函数作为参数
- 接收第二个参数(依赖列表),只有依赖更新时,才会执行函数
- 返回一个函数。先执行返回函数,再执行参数函数
- 引入
-
示例
import React, { useState, useEffect, useLayoutEffect } from 'react' function EffectFunction() { const [num, setNum] = useState(1) useLayoutEffect(() => { console.log("useLayoutEffect") document.body.addEventListener('layout', () => { }) return () => { document.body.removeEventListener('layout', () => { }) } }, [num]) useEffect(() => { console.log("useEffect") document.body.addEventListener('todo', () => { }) return () => { document.body.removeEventListener('todo', () => { }) } }, [num]) return ( <div onClick={() => setNum(num => num + 1)}> 这是一个函数组件-{num} </div> ) } export default EffectFunction
-
useEffect与useLayoutEffect区别
- useEffect执行时机在render之后
- useLayoutEffect是在dom更新之后
useMemo-组件跟随状态更新
-
概述
当父组件因为自身状态改变而重新渲染时,会带动子组件的重新渲染,及时此时子组件的状态并没有发生改变,为了解决子组件的非必要渲染,可以使用memo函数来包装子组件,这样当子组件的props没有发生改变时,及时父组件重新渲染了,子组件也不会重新渲染。
-
作用
- 让组件中的函数跟随状态更新
-
注意事项
- 优化函数组件中的功能函数
-
使用步骤
- 引入
import React,{useMemo} from "react"
- 接收一个函数作为参数
- 接收第二个参数(依赖列表),只有依赖更新时,才会执行函数
- 返回的是一个数值
- 引入
-
适用场景
- 有些计算开销很大,就需要「记住」它的返回值,避免每次render都去重新计算。
- 由于值的引用发生变化,导致下游组件重新渲染,也需要「记住」这个值。
-
示例
import React, { useState, useEffect, useMemo } from 'react' function EffectFunction() { const [num, setNum] = useState(1) const [age, setAge] = useState(18) // function getDoubleNum(){ // console.log("获取双倍的num") // return 2 * num // } //useMemo使用方法 //1.接收一个函数作为参数 //2.第二个参数为依赖列表 //3.返回的是一个数值 const getDoubleNum = useMemo(()=>{ console.log("获取双倍的num") return 2*num },[num]) return ( <div onClick={() => setNum(num => num + 1)}> 这是一个函数组件-{getDoubleNum},age的值为----{age} </div> ) } export default EffectFunction
useCallback-组件跟随状态更新
- 作用
- 跟随状态更新执行
- 注意事项
- 只有依赖项改变的时候才会执行
- 使用场景
- 在组件内部,那些会成为其他useEffect依赖项的方法,建议用 useCallback 包裹,或者直接编写在引用它的useEffect中 这种情况通常出现在reset等等这些函数,可能还有多个地方调用这个函数。
- 如果你的function会作为props传递给子组件,请一定要使用 useCallback 包裹,对于子组件来说,如果每次render都会导致你传递的函数发生变化,可能会对它造成非常大的困扰。同时也不利于react做渲染优化。
useRef-长久保存数据
-
概述
useRef
返回的 ref 对象在组件的整个生命周期内保持不变,也就是说每次重新渲染函数组件时,返回的ref 对象都是同一个。从概念上讲,可以认为 refs 就像是一个 class 的实例变量。
-useRef
不仅仅是用来管理DOM ref的它可以存放任何变量,更改.current
属性不会导致重新渲染。
-
作用
- 长久保存数据
-
注意事项
- 返回一个子元素索引,此索引在整个生命周期中保持不变
- 对象发生改变,不通知。属性变更不重新渲染
-
使用场景
- 希望一个变量在这个组件中,即使组建重新渲染,这个值也不会变。
- 保存子组件提供的某一个值(可能是一个DOM元素,也可能就是一个普通对象)
-
示例
import React, { useState, useEffect, useRef } from 'react' function EffectFunction() { const [num, setNum] = useState(1) const ref = useRef() //1.保存一个值,在整个生命周期中维持不变 //2.重新赋值ref.current不会触发重新渲染 useEffect(() => { ref.current = setInterval(() => { setNum(num => num + 1) }, 400) }, []) useEffect(() => { if (num > 10) { console.log("超过10了", ref.current) clearInterval(ref.current) } }, [num]) return ( <div> 这是一个函数组件-{num} </div> ) } export default EffectFunction
useContext-组件之间共享状态
-
作用
- 带着子组件去更新(游荡或者流浪)
-
注意事项
- 上层数据发生改变,肯定会触发重新渲染
- 当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染
- react 中数据是通过 props 属性自上而下(由父及子)进行传递的,但这种做法对于多层级父子关系的组件传值是极其繁琐的。react 提供了
context
api 来实现在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。React 16.3之后的context
api 较之前的好用 Context.Provider
,接收一个 value 属性,传递给消费组件。一个 Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据
-
使用步骤(单数据源、父组件向子组件通信)
- 需要引入
useContext
、createContext
两个内容 - 通过
createContext
创建一个context句柄 Context.Provider
来确定数据共享范围- 通过
value
来分发内容 - 在子组件中,通过
useContext(context句柄)
来获取数据
- 需要引入
-
使用
Context.Provider
,具体使用方法如下:- 使用
createContext()
创建一个context 对象(内容为需要传递的数据),参数为默认值; - 在父组件外层使用
<.Provider>
将当前 context 的值传递给内部所有的组件树。当使用了<.Provider>
后,不再读取上面的默认值,需要设置 value 属性来进行数据传递。 - 当组件需要读取数据时,指定
contextType
读取当前的 context 对象(即刚开始创建的 context); - 通过
useContext(context句柄)
,获取到当前 context 内的数据
- 使用
-
示例
import React, { useState, useContext, createContext } from 'react' //1.需要引入useContext、createContext两个内容 //2.通过createContext创建一个context句柄 //3.Conext.Provider来确定数据共享范围 //4.通过value来分发内容 //5.在子组件中,通过useContext(context句柄)来获取数据 const ShowNumContext = createContext(null) function EffectFunction() { const [num, setNum] = useState(1) return ( <div> 这是一个函数组件-{num} // 使用Provider 将当前context的值传递给下面的组件树 <ShowNumContext.Provider value={num}> <Item1></Item1> <Item2></Item2> </ShowNumContext.Provider> </div> ) } /** * 子组件1 */ function Item1() { //指定contextType 读取当前的context //react 会往上找到最近的 Provider,然后使用它的值 const num = useContext(ShowNumContext) return ( <div> 子组件{num} </div> ) } /** * 子组件2 */ function Item2() { const num = useContext(ShowNumContext) return ( <div> 子组件{num + 2} </div> ) } export default EffectFunction
上面的方法中,我们实现了跨组件的数据传递,这种方式的缺点是只能有一个共享的数据源,也就是在 Item1 组件
和Item2组件
中,指定 num的值只能是一个
。那么如果我们有多个数据源都需要进行跨组件传递,应该怎么做呢?可以使用 <.Consumer>
来实现对多个数据源进行共享(多数据源跨组件通信)
-
Context的使用场景
Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。谨慎使用,因为这会使得组件的复用性变差。如果用组件组合可以解决的问题,就不要使用 Context 。
使用 context 的通用的场景包括管理当前的 locale,theme,或者一些缓存数据。
useReducer-复杂逻辑简单化
-
作用
- 去其他地方借用资源
-
使用步骤
- 需要创建数据仓库store和管理者reducer
- 通过
useReducer(reducer,store)
来获取state和dispatch
-
注意事项
- 函数组件的redux的操作
-
示例
import React, { useReducer } from 'react' //redux必须的内容——store,reducer //useReducer使用方法 //1.需要创建 数据仓库store和管理者reducer //2.通过useReducer(reducer,store)来获取state和dispatch const store = { num: 10 } const reducer = (state, action) => { switch (action.type) { case "changeNum": return { ...state, num: action.num } default: return { ...state } } } function EffectFunction() { const [state, dispatch] = useReducer(reducer, store) return ( <div onClick={() => { dispatch({ type: 'changeNum', num: 100 }) }}> 这是一个函数组件-{state.num} </div> ) } export default EffectFunction
自定义Hooks
-
作用
- 自定义Hooks以支持特殊场景
-
使用步骤
- 引入react和自己需要的hook
- 创建自己的hook函数
- 返回一个数组,数组中第一个内容是数据,第二个是修改数据的函数
- 将自己定义的hook函数暴露出去
- 在自己的业务组件中,引入并使用
-
示例
import React, { useState, useEffect } from 'react' /** * 自定义Hooks步骤 * 1.引入react和自己需要的hook * 2.创建自己的hook函数 * 3.返回一个数组,数组中第一个内容是数据,第二个是修改数据的函数 * 4.将自己定义的hook函数暴露出去 * 5.在自己的业务组件中,引入并使用 */ //如何实现模拟的数据接口请求功能 function userLoadData() { const [num, setNum] = useState(1) useEffect(() => { setTimeout(() => { setNum(2) }, 1000) }, []) return [ num, setNum ] } export default userLoadData