欢迎来到DIVCSS5查找CSS资料与学习DIV CSS布局技术!
故事的源头
 
小明想做一个 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