React Hooks 渲染流程理解

458 阅读2分钟

问题描述

在用了一段时间的 React hooks 后, 对于页面渲染的执行时机,个人感觉是不好把控的。举个例子:


// 父组件
const App = () =>{
    const [data,setData] = useState({})

    useEffect(() =>{
        // 模拟请求数据
        new Promise(() => {
            setTimeout(() => {
                setData({
                    name: 'zzz'
                })
            },3000)
        })
    },[])

    return <>
        <Children data={data} />
    </>
}


// 子组件
const Children = ({data}) => {

    return <>
        <p>{ data.name }</p>
    </>
}

上面这段代码是日常开发中最常见的需求, 父组件查询服务后传给子组件显示, 但是由于 react hooks 只能依赖 useEffect 更新渲染, 没有具体的生命周期控制其渲染。
因此在 CSR(Client-Side Rendering,客户端渲染)useEffect 中请求数据时,会在数据返回前页面出现白屏状态,目前解决方法子组件 effct 监听传过来的值,但这并不是高效的方法,这是迫使父子组件都发生不必要的更新和监听。

// 子组件
const Children = ({data}) => {
    const [_data,setData] = useState('')

    useEffect(() => {
        setData(data)
    }, [data])

    return <>
        <p>{ data.name }</p>
    </>
}

原因

我们看下父子组件都依赖 useEffect 获取初始数据渲染流程就能明白为什么这样了,那么整个渲染流程如下

  1. 父组件 mount

  2. 父组件 useEffect 执行,请求数据

  3. 数据返回后重新 mount

  4. 子组件 mount

  5. 子组件 useEffect 执行,请求数据

  6. 数据返回后重新渲染子组件(若有父组件传来的数据,还另外需要监听重新渲染子组件)

这就是渲染中的瀑布问题, 数据像瀑布一样一级一级向下流动,流到的组件才开始渲染,很低效。

解决方法

目前官方没有具体的钩子解决此类问题,只能依靠社区的方法

对于 SSR,可以使用 Next.js、Remix 接管数据请求。

对于 CSR,可以使用 React Query、useSWR 接管数据请求。