现代React生命周期(Hooks)

目录

useState --- 状态管理核心

useEffect --- 副作用处理

useLayoutEffect --- 同步dom操作

useCallback --- 缓存函数

useMemo --- 缓存计算结果

useRef --- 引用dom/ 持久化值

useContext --- 跨组件状态共享

useReducer --- 复杂状态管理

自定义hooks

补充内容


 

首先要弄清的问题

  • 函数式组件不存在 this,因为它就是一个普通的 JavaScript 函数,而不是一个类的实例。
  • 函数式组件的核心思想是:一个纯粹的函数,接收 props,返回 React 元素。 它就像一个数学函数 f(x) = y 一样,输入确定,输出就确定。
  • 函数式组件是把整个函数当做render函数,每次修改状态就会重新执行函数式组件!!!!!

让函数式组件站起来!!!

从 React 16.8 开始,Hooks 成为了编写 React 组件的首选方式。它没有类组件那样的生命周期方法,而是提供了一些函数来让你在函数组件中 “钩入” React 的状态和生命周期。

 

useState --- 状态管理核心

作用:给函数组件添加 可修改的状态 ,替代类组件的this.state

第一个参数就是存储的状态,第二个参数是改变状态的方法

改变状态的逻辑:直接覆盖,不是合并

const [name,setName] = useState("kerwin")

return (
    <div>
        <button onClick={()=>{
                setName("xiaoming")
            }}>click</button>
        app-{name}
    </div>
)

注意:不能在函数式组件内部修改state!(原因前面有提到)

useEffect --- 副作用处理

作用:处理组件副作用(数据请求、DOM操作、订阅/取消订阅等),替代类组件的componentDidMount / componentDidUpdate / componentWillUnmount

场景:初始化请求数据,监听窗口大小,清理定时器

核心规则:

第二个参数deps数组

  1. 空数组:仅挂载时执行
  2. 指定变量:变量变化时执行(浅层比较)
  3. 无参数:每次渲染都执行

返回函数:清理副作用(组件卸载/ 下一次触发前执行)

都用hooks了,当然可以写多次。

注意:

useEffect 的依赖数组不进行深度监听(Deep Comparison)。它只进行浅层比较(Shallow Comparison)。这意味着,React 只会检查依赖数组中的引用是否发生了变化,而不会递归地检查对象内部的每一个属性。

useLayoutEffect --- 同步dom操作

作用:与useEffect用法完全一致,但执行时机不同  --- 在dom渲染完成后,浏览器绘制之前同步执行

场景:需要修改dom并立即生效的场景(如,测量dom尺寸后调整位置,避免页面闪烁)

页面闪烁原因:useEffect 在绘制后执行,修改状态触发二次渲染,导致页面重新绘制。

使用useLayoutEffect避免闪烁原因:useLayoutEffect 在绘制前执行,修改状态后 React 会合并这次更新,只有一次回流、重绘的代价。

不要滥用 useLayoutEffect:若无需修改 DOM 或不担心闪烁,优先用 useEffect,避免不必要的性能损耗。

更直观的对比:

useEffect 执行流程

1. 组件触发渲染(初始渲染/状态更新)
2. React 计算新的 DOM 树 → 将 DOM 挂载到页面
3. 浏览器绘制页面(用户看到更新后的 UI)
4. 异步执行 `useEffect` 回调(不阻塞步骤 3)
5. 依赖变化时,先执行上一轮的清理函数 → 再执行新回调

useLayoutEffect 执行流程

1. 组件触发渲染(初始渲染/状态更新)
2. React 计算新的 DOM 树 → 将 DOM 挂载到页面
3. 同步执行 `useLayoutEffect` 回调(阻塞步骤 4,必须等回调执行完)
4. 浏览器绘制页面(用户看到最终 UI)
5. 依赖变化时,先执行上一轮的清理函数 → 再执行新回调

useCallback --- 缓存函数

作用:缓存函数引用,避免组件每次渲染时重新创建相同函数(优先子组件性能,尤其配合React.memo使用)

场景:传递给子组件的回调函数(防止子组件不必要重绘)

防止因为组件重新渲染,导致方法被重新创建,起到缓存作用。只有第二个参数变化了,才重新声明一次

var handleClick = useCallback(()=>{
    console.log(name) 
},[name])
​
<button onClick={()=>handleClick()}>hello</button>

分析:

传入name依赖,则只有当name改变时,这个函数才会被重新声明一次

如果传入空数组,那么就是第一次创建后就被缓存,如果name后期改变了,拿到的还是原来的name

如果不传第二个参数,每次都会重新声明一次

【约等于vue watch:均为[ 监听依赖变化 ],执行副作用】

useMemo --- 缓存计算结果

作用:缓存昂贵的计算结果,避免组件每次渲染时重复计算(优化性能)

场景:大数据排序、复杂数学计算、格式化大量数据

useMemo和useCallback的区别

  • useCallback: 记忆化的是一个函数

  • useMemo: 记忆化的是一个函数的返回值

useCallback的功能完全可以有useMemo所取代。如果你想通过使用useMemo返回一个记忆函数也是完全可以的。

// 这两行代码在功能上是等价的
const handleDel = useCallback(
    (index)=>{
        console.log(index)
        var newlist = [...list]
        newlist.splice(index,1)
        setList(newlist)
    },
    [list]
)
const handleDel = useMemo(
    ()=>(index)=>{
        console.log(index)
        var newlist = [...list]
        newlist.splice(index,1)
        setList(newlist)
    },
    [list]
)

唯一的区别是:useCallBack不会执行第一个参数函数,而是将它返回给你,而useMemo会执行第一个函数并且将函数执行结果返回给你。所以在前面的例子中,可以返回handleClick来达到存储函数的目的

所以useCallback常用记忆事件函数,生成记忆后的事件函数并传递给子组件使用。而useMemo更适合经过函数计算得到一个确定的值,比如记忆组件。

重要提示: 不要滥用这两个 Hook。记忆化本身也有开销(需要存储和比较依赖项)。对于非常简单的计算或不常渲染的组件,直接重新计算或创建新函数的性能开销可能比使用记忆化 Hook 更小。只有在你确定存在性能瓶颈时,才进行优化。

【约等于vue computed:均为[ 缓存计算结果 ],依赖变化时更新】

useRef --- 引用dom/ 持久化值

作用:2种

  1. 直接引用dom元素
  2. 存储 跨渲染不重置 的变量(不触发组件重绘)

场景:操作dom(聚焦输入框),保存定时器id,缓存前一次状态

代替类组件的React.createRef()

const myswiper = useRef(null)
<Swiper ref={myswiper} />

useContext --- 跨组件状态共享

作用:接收createContext创建的上下文,直接获取上层组件传递的状态,无需逐层props传递

场景:全局状态共享(如主题、用户登录状态、语言设置)

用useContext代替GlobalContext.Consumer,可以减少代码的复杂度(之前的写法也可以用)

const GlobalContext = React.createContext()
​
function FilmDetail(){
    //此处的value就是Provider定义的对象value
    const value = useContext(GlobalContext)
    return <div className="filmdetail">
        detail---{value.info}
    </div>
}

useReducer --- 复杂状态管理

作用:通过 reducer函数 管理状态,类似Redux核心思想,替代复杂逻辑的useState(如多状态联动、复杂条件修改状态)

场景:购物车(增删改查)、表单多字段验证、状态逻辑复杂的组件

第一个参数:函数,专门在外面管理状态的

第二个参数:初始状态值

reducer可以理解成任务处理器,组件使用dispatch来向reducer来分配任务,reducer处理组件发布的任务。

const [state, dispatch] = useReducer(reducer, initialState)
const initialState = {
    value1: 0,
    value2: 0
}
const reducer = (prevState, action) => {
    let newState = { ...prevState }
    switch (action.type) {
        case 'add1':
            newState.value1 = action.value
            //要有返回值,不可以用break
            return newState
        case 'add2':
            newState.value2 = action.value
            return newState
        default:
            return prevState
    }
}

将状态管理逻辑提出去,与视图逻辑分开。使组件无状态,实现低耦合

reducer跟redux的区别是,dispatch action。这个行为,redux这个行为能消化异步,这里不能消化异步

自定义hooks

组件可以复用UI,但自定义hooks主要复用逻辑

必须以“use”开头。这个约定非常重要。不遵循的话,由于无法判断某个函数是否包含对其内部Hooks的调用,React将无法自动检查你的Hooks是否违反了Hooks的规则

function useCinemaList(){
    const [cinemaList,setcinemaList] = useState([])
    //写逻辑
    return {
        cinemaList
    }
}
​
export default App(){
    const {cinemaList} = useCinemaList()
    return (
        <div>写视图</div>
    )
}

补充内容

1 vscode中的快捷键

rfc  --- 快速创建函数式组件

uss  ---  useState

2 钩子函数的引入

方法一:直接使用

const [count, setCount] = React.useState(0); // 无需单独引入 useState

方法二:单独引入(推荐)

import React, { useState } from 'react'; // 显式解构引入
const [count, setCount] = useState(0);

3 hooks使用规则

  1. 只能在函数组件顶层调用hooks(不能在 if/for/嵌套函数 中使用)
  2. 只能在React函数组件(通常首字母大写)或自定义hooks中调用(不能再普通js函数中使用)

官方描述:

Only call Hooks at the top level

Only call Hooks from React function components or custom Hooks

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值