问题描述
在用了一段时间的 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 获取初始数据渲染流程就能明白为什么这样了,那么整个渲染流程如下
-
父组件 mount
-
父组件 useEffect 执行,请求数据
-
数据返回后重新 mount
-
子组件 mount
-
子组件 useEffect 执行,请求数据
-
数据返回后重新渲染子组件(若有父组件传来的数据,还另外需要监听重新渲染子组件)
这就是渲染中的瀑布问题, 数据像瀑布一样一级一级向下流动,流到的组件才开始渲染,很低效。
解决方法
目前官方没有具体的钩子解决此类问题,只能依靠社区的方法
对于 SSR,可以使用 Next.js、Remix 接管数据请求。
对于 CSR,可以使用 React Query、useSWR 接管数据请求。