故事的源头
小明想做一个 mini 版的 Facebook,现在他先通过接口获取到了当前用户的信息,并展示在页面上:
function App(props) {
const me = useAsyncMemo(() => (
api.get('/user/me')
), [])
return (
<div>
<h1>Hi, {me.name}!</h1>
</div>
)
}
这里使用了 useAsyncMemo 进行异步数据的获取,详见这篇文章
看起来没什么问题?然而小明发现报错了:
Cannot read property 'name' of null
噢, me 是异步获取的数据,因此,我们需要对它进行判空处理:
<h1>Hi, {me && me.name}!</h1>
可是小明又发现,当接口响应比较慢时,页面会显示成下面这样:
Hi, !
看来,还是整个组件都不进行渲染比较好:
return me && (
<div>
<h1>Hi, {me.name}!</h1>
</div>
)
现在总算是达到了比较令人满意的地步。
判空的泛滥
下面,小明又为这个组件加上了好友列表:
function App(props) {
const me = useAsyncMemo(() => (
api.get('/user/me')
), [])
const friends = useAsyncMemo(() => (
api.get(——/user/${me.id}/friends/——)
), [me.id])
return me && (
<div>
<h1>Hi, {me.name}!</h1>
<p>You have {friends.length} friends.</p>
<ul>
{friends.map((friend) => (
<li key={friend.id}>{friend.name}</li>
))}
</ul>
</div>
)
}
这次小明及时发现了问题:又是判空处理!一番改动之后,小明已经心力憔悴,组件也已经面目全非:
function App(props) {
const me = useAsyncMemo(() => (
api.get('/user/me')
), [])
const friends = useAsyncMemo(() => {
if (!me) return // 判空
return api.get(——/user/${me.id}/friends/——)
}, [me && me.id]) // 判空
return me && ( // 判空
<div>
<h1>Hi, {me.name}!</h1>
{friends && ( // 判空
<p>You have {friends.length} friends.</p>
)}
<ul>
{friends && friends.map((friend) => ( // 判空
<li key={friend.id}>{friend.name}</li>
))}
</ul>
</div>
)
}
望着满屏的判空,小明心有不甘,于是,他试图使用一条经典的重构策略“判断前置”:
function App(props) {
const me = useAsyncMemo(() => (
api.get('/user/me')
), [])
if (!me) return null // 提前中止组件的渲染
// 现在 me 一定是非空的了
const friends = useAsyncMemo(() => (
api.get(——/user/${me.id}/friends/——)
), [me.id])
if (!friends) return null
// 现在 friends 一定是非空的了
return (
<div>
<h1>Hi, {me.name}!</h1>
<p>You have {friends.length} friends.</p>
<ul>
{friends.map((friend) => (
<li key={friend.id}>{friend.name}</li>
))}
</ul>
</div>
)
}
本以为大功告成,小明却沮丧的发现遇到了报错:
Rendered more hooks than during the previous render
这又是怎么回事呢?查阅了一番资料后,小明意识到他违反了 React Hooks 的一条规定:
Only Call Hooks at the Top Level By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple useState and useEffect calls.
if (!x) return null 的这种写法虽然解决了判空泛滥的问题,但却导致了组件在前后两次渲染中执行的 Hooks 不一致。这可怎么办呢……小明陷入了沉思。
如需转载,请注明文章出处和来源网址:http://www.divcss5.com/html/h63670.shtml