Skip to content

children属性引发的React.memo缓存失效

复现场景

ts
const Child: React.FC<{
    children: React.ReactNode
}> = memo(({ children }) => {
    console.log('渲染子组件')
    return <div>{children}</div>
})

const Test: React.FC= () => {
    const [count, setCount] = useState(0)
    const onClick = () => {
        setCount(count + 1)
    }

    return (
        <>
            <button onClick={onClick}>测试{count}</button>
            <Child>
                <div>12</div>
            </Child>
        </>
    )
}

上面代码每次点击按钮时,虽然看起来数据没有变动,但是Child组件都会重新渲染

问题分析

这里虽然Child标签上没明确传递任何属性,但实际上<div>12</div>这部分代码,会作为props.children传递给子组件的。 memo缓存之所以失效,得根据memo的缓存原理去分析,memo底层是通过比较之前的props和当前的props是否相等,如果不相等,就会重新渲染。

那么上面例子中,点击按钮,修改状态后,触发了父组件的重新渲染,此时props.children是否改变了呢?

这里则需要分析<div>12</div>干了什么事。<div>12</div>其本质是代码编译后,会转换成React.createElement,然后React.createElement会返回children对象,当重新渲染时,返回的children对象引用变了,所以memo缓存失效了。弄懂问题根源,问题就好解决了,就是给<div>12</div>加个缓存

处理方式

使用useMemo缓存子组件,进行传递。这样memo就能符合我们预期执行

ts
const Child: React.FC<{
    children: React.ReactNode
}> = memo(({ children }) => {
    console.log('渲染子组件')
    return <div>{children}</div>
})

const Test: React.FC= () => {
    const [count, setCount] = useState(0)
    const onClick = () => {
        setCount(count + 1)
    }

    const child = useMemo(() => <div>12</div>, []) // 对传入的children节点进行缓存

    return (
        <>
            <button onClick={onClick}>测试{count}</button>
            <Child>
                {child}
            </Child>
        </>
    )
}

苏ICP备20040768号