2024年3月13日
By: Chase

聊聊React的新特性们[draft]

前言

上次面试, 聊到React近几年更新的大版本, 我才意识到我比较深入的认知还停留在Hooks刚出来的时候, 近期对社区的关注也就是只是停留在知道出了18.2, 并且已经全面拥抱Next.js了.

比起年龄, 更让程序员焦虑的, 是对新变化的不了解.

本文不是为了详细讲解新的hooks, 讲解肯定是官网最详细, 主要还是写一些demo帮助自己理解.

在blog中引入18.2


        console.log('React version:', React.version)
        console.log('ReactDOM version:', ReactDOM.version)
    

useLayoutEffect

这是个17.几的版本就出来的特性了, 一直没用过.

官网的描述: useLayoutEffect 是 useEffect 的一个版本,在浏览器重新绘制屏幕之前触发。

为了证明useLayoutEffect对渲染有阻拦作用, 写了下面一个简单的demo:

鼠标点击红框内即加载小蓝框, 点击绿框即隐藏小蓝框.

为了能看出差别, 加了一个比较重的循环, 多点几次, 在把示例代码改成useEffect可以明显看到有闪烁.

const { createRoot } = ReactDOM
const { useEffect, useLayoutEffect, useState } = React

const root = createRoot(document.getElementById('useLayoutEffect'))

const ChildDom = ({ position }) => {
    const [state, setState] = useState({ x: 0, y: 0 })

    // !!!!把这改成useEffect试试!!!!
    useLayoutEffect(() => {
        for (let i = 0; i < 1e8; i += 1) {
            const a = Math.random()
        }
        setState(position)
    }, [])

    return (
        <div style={{
            position: 'absolute',
            left: state.x,
            top: state.y,
            width: '100px',
            height: '100px',
            background: 'blue'
        }}>
            child
        </div>
    )
}

const App = () => {
	const [childPosition, setChildPosition] = useState(null)

    return (
		<div
            style={{
                display: 'flex'
            }}
        >
            <div
                onClick={e => {
                    setChildPosition({
                        x: e.nativeEvent.offsetX,
                        y: e.nativeEvent.offsetY
                    })
                }}
                style={{
                    border: '1px solid red',
                    width: '200px',
                    height: '200px',
                    position: 'relative'
                }}
            >
                click to show area
                
                {childPosition && (
                    <ChildDom
                        position={childPosition}
                    />
                )}
            </div>

            <div
                onClick={() => setChildPosition(null)}
                style={{
                    border: '1px solid green',
                    width: '200px',
                    height: '200px',
                    position: 'relative'
                }}
            >
                click to disappear area
            </div>
        </div>
    )
}

root.render(<App />);

这个功能我知道但是一直没用过确实是有原因的, 一时半会想不出使用的业务场景. 比如上面我写的demo, 把组件内部的初始stateset值的那一步完全可以想办法规避掉.

18的重头戏

react18讲的最多的就是并发渲染, 解决的是啥问题呢, 上个demo先:

const { createRoot } = ReactDOM
const { useEffect, useLayoutEffect, useState } = React

const root = createRoot(document.getElementById('demo'))

const ChildA = () => {
    console.log('renderA')

    return (
        <div>
            childA
        </div>
    )
}

const ChildB = () => {
    console.log('render heavy B')

    return (
        <div>
            {new Array(1e5).fill(0).map((_, i) => (
                <div key={i}>
                    childB
                </div>
            ))}
        </div>
    )
}

const App = () => {
    const [activeA, setActiveA] = useState(true)

    const handleClickButton = (param) => {
        setActiveA(param)
    }

    return (
        <div>
            <button
                style={{ color: activeA ? 'red' : 'black' }}
                onClick={() => handleClickButton(true)}
            >
                activate A
            </button>
            <button
                style={{ color: !activeA ? 'red' : 'black' }}
                onClick={() => handleClickButton(false)}
            >
                activate B
            </button>

            {activeA ? <ChildA /> : <ChildB />}
        </div>
    )
}

root.render(<App />);

useTransition

useDeferredValue

Tags: React useDeferredValue useTransition useLayoutEffect