useEffect从入门到入土

目录

1.useEffect

2. useEffectEvent

3.  useLayoutEffect

4.useInsertionEffect


1.useEffect

副作用是相对于纯函数概念来说的, 除事件回调处理副作用,其他副作用尽量放在useEffect中;

  •     组件首次渲染、有依赖项更新(Object.is方法判断)时,该useEffect触发
  •     jsx渲染完成后立马触发useEffect,也是当前作用域下最后执行的内容
  •     若不传依赖项,每次都触发;若依赖为[]则只首次渲染触发
const App = () => {

    const [count, setCount] = useState(0)
    const [msg, setMsg] = useState('msg')
    // 除事件回调处理副作用,其他副作用尽量放在useEffect
    const handclick = () => {
        setCount(count + 1)
    }
    // jsx渲染完成后立马触发useEffect,也是当前作用域下最后执行的内容
    // 组件首次渲染、有依赖项更新(Object.is方法判断)时,该useEffect触发
    // 若不传依赖项,每次都触发;若依赖为[]则只首次渲染触发
    useEffect(() => {
        const logCount = () => {
            console.log(count);
        }
        logCount()
    }, [count])
    useEffect(() => {
        console.log(msg);
    }, [msg])
    useEffect(() => {
        console.log(123);
    },)

    return <>
        hello useEffect
        <button onClick={handclick}> 点击count加1 </button>
    </>

}

useEffect清理工作:return在卸载组件时;下一次更新前清理作用域


import { useState } from "react"
import { useEffect } from "react"

function FetchChat(title) {
    const delay = title === '情感聊天室' ? 2000 : 200
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve([
                { id: 1, text: title + '1' },
                { id: 12, text: title + '12' },
                { id: 13, text: title + '13 ' },
            ])
        }, delay)
    })
}

 function Chat({ title }) {
    const [list, setList] = useState([])
    let ignore = false
    useEffect(() => {
        console.log('进入', title);
        FetchChat(title).then((data) => {
            if (!ignore) {
                setList(data)
            }
        })
        // useEffect的清理工作 在本次最后组件卸载时触发
        // 防止上次操作的作用残留
        return () => {
            console.log('退出', title);
            ignore = true
        }
    }, [title])
    return (<>
        hello Chat
        <ul>
            {list.map((item) => <li key={item.id}>{item.text}</li>)}
        </ul>
    </>)
}

// 同上Chat
// function Chat({ title }) {
//     const [list, setList] = useState([])
//     useEffect(() => {
//         console.log('进入', title);
//         let ignore = false
//         async function fetchData() {

//             const data = await FetchChat(title)
//             if (!ignore)
//                 setList(data)
//         }
//         fetchData()

//         // useEffect的清理工作 在本次最后组件卸载时触发
//         // 防止上次操作的作用残留
//         return () => {
//             console.log('退出', title);
//             ignore = true
//         }
//     }, [title])
//     return (<>
//         hello Chat
//         <ul>
//             {list.map((item) => <li key={item.id}>{item.text}</li>)}
//         </ul>
//     </>)
// }


const App = () => {
    const [show, setShow] = useState(true)
    const [title, setTitle] = useState('情感聊天室')

    const handclick = () => {
        setShow(!show)
    }
    const handChange = (e) => {
        setTitle(e.target.value)
    }

    return (<>
        hello App...........
        {show && <Chat title={title} />}
        <button onClick={handclick}>开关聊天室</button>
        <select value={title} onChange={handChange}>
            <option value="情感聊天室">情感聊天室</option>
            <option value="体育聊天室">体育聊天室</option>
        </select>


    </>)
}
export default App

2. useEffectEvent

useEffectEvent:实验钩子,作用是从副作用中提取非反应性逻辑(变量改变但不触发页面渲染)

react18.2中还没该钩子 需单独引 react@experimental 、 react-dom@experimental

const {useState,useEffect,experimental_useEffectEvent} = React

function ChatRoom({title,theme}){
    const themeEffectEvent =  experimental_useEffectEvent(()=>{
    console.log('主题:',theme)
})
useEffect(()=>{
    console.log('进入:',title)
    themeEffectEvent()
    return ()=>{
        console.log('退出:',title)
}
},[title])

return <>聊天室</>
}

3.  useLayoutEffect

 useLayoutEffect :组件第一次进行渲染时并不会立马完成,程序走到 useLayoutEffect 会触发第二次件加载,第一次useLayoutEffect 中的setState已经完成,会在第二次触发组件渲染时合并第一次的state,从而呈现最终的状态(不会闪屏)。

 在当前某次渲染时,看着是useLayoutEffect先于useEffect,但是在多次渲染时useLayoutEffect同步会导致页面渲染效率变低(在某个操作很耗时的情况下特别明显),而useEffect异步会把耗时操作执行结果放在下次渲染,本次会把同步操作的结果先展示在页面上。

import { useState, useEffect, useLayoutEffect } from "react";

function App() {
    const [msg, setMsg] = useState(' hello App')

    // useEffect(() => {
    //     console.log('useEffect');
    // })
    // // useLayoutEffect 先于 useEffect触发
    // useLayoutEffect(() => {
    //     console.log('useLayoutEffect');
    // })

    // useEffect(() => {
    //     // 页面会出现闪屏  hello App => hello useEffect
    //     for (let i = 0; i < 10000000; i++) {
    //         setMsg('hello useEffect')
    //     }
    // })
    // 不会闪屏 直接显示hello useLayoutEffect 
    useLayoutEffect(() => {
        for (let i = 0; i < 10000000; i++) {
            setMsg('hello useLayoutEffect')
        }
    })
    return <>{msg}</>
}
export default App

结论:useEffect 是异步的,在组件渲染绘制到屏幕之后执行,而 useLayoutEffect 生效于渲染之后但在屏幕更新之前,同步执行状态更新。

大部分情况用 useEffect 性能更好,但当 useEffect 里面需要操作dom并改变样式,需要用useLayoutEffect避免闪屏。

4.useInsertionEffect

useInsertionEffect: 触发时机在渲染之前,拿不到dom,用于css-in-js且采用运行时style标签注入的情况

import { useRef, useInsertionEffect, useLayoutEffect, useEffect } from "react";
function App() {
    const myRef = useRef(null)
    useEffect(() => {
        console.log(3, myRef.current);
    })
    useLayoutEffect(() => {
        console.log(2, myRef.current);
    })

    useInsertionEffect(() => {
        // 拿不到dom 说明触发时机在渲染之前
        console.log(1, myRef.current);
        const style = document.createElement('style')
        style.innerHTML = `
            .box{
             bcakground:'red';
             width:500px;
             heigth:100px;
            }`
        document.head.appendChild(style)

    })
    return (<div>
        hello app
        <div className="box" ref={myRef}>aaaa</div>
    </div>)
}
export default App

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白目

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值