React Hooks 在 SSR 模式下常见问题及解决方案

开发 前端
由于 SSR 是在非浏览器环境执行 JS 代码,所以会出现很多问题。本文主要介绍 React Hooks 在 SSR 模式下常见问题及解决方案。

[[400053]]

服务端渲染(Server-Side Rendering),是指由服务侧完成页面的 HTML 结构拼接的页面处理技术。一般用于解决 SEO 问题和首屏加载速度问题。

由于 SSR 是在非浏览器环境执行 JS 代码,所以会出现很多问题。本文主要介绍 React Hooks 在 SSR 模式下常见问题及解决方案。

更多关于 SSR 的介绍可以看 UmiJS 的文档《服务端渲染(SSR)[1]》。

问题一:DOM/BOM 缺失

SSR 是在 node 环境下运行 React 代码,而此时 window、document、navigator 等全局属性没有。如果直接使用了这些属性,就会报错 window is not defined, document is not defined, navigator is not defined 等。

常见的错误用法是在 Hooks 执行过程中,直接使用了 document 等全局属性。

  1. import React, { useState } from 'react'
  2.  
  3. export default () => { 
  4.   const [state, setState] = useState(document.visibilityState); 
  5.   return state; 

解决方案

1.将访问 DOM/BOM 的方法放在 useEffect/useLayoutEffect 中(服务端不会执行),避免服务端执行时报错,例如:

  1. import React, { useState, useEffect } from 'react'
  2.  
  3. export default () => { 
  4.   const [state, setState] = useState(); 
  5.    
  6.   useEffect(()=>{ 
  7.     setState(document.visibilityState); 
  8.   }, []); 
  9.    
  10.   return state; 

2.通过 isBrowser[2] 来做环境判断

  1. import React, { useState } from 'react'
  2.  
  3. function isBrowser() { 
  4.   return !!(typeof window !== 'undefined' && window.document && window.document.createElement); 
  5.  
  6. export default () => { 
  7.   const [state, setState] = useState(isBrowser() && document.visibilityState); 
  8.    
  9.   return state; 

问题二 useLayoutEffect Warning

如果使用了 useLayoutEffect,在 SSR 模式下,会出现以下警告

  • ⚠️ Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer's output format. This will lead to a mismatch between the initial, non-hydrated UI and the intended UI. To avoid this, useLayoutEffect should only be used in components that render exclusively on the client. See https://fb.me/react-uselayouteffect-SSR for common fixes.

解决方案

  1. 使用 useEffect 代替 useLayoutEffect(废话)
  2. 根据环境动态的指定是使用 useEffect 还是 useLayoutEffect。这是来自社区的一种 hack 解决方案,目前在 react-redux[3]react-use[4]react-beautiful-dnd[5] 均使用的这种方案。
  1. import { useLayoutEffect, useEffect } from 'react'
  2. const useIsomorphicLayoutEffect = isBrowser() ? useLayoutEffect : useEffect; 
  3. export default useIsomorphicLayoutEffect; 

总结:写 Hooks 时需要注意

1.不要在非 useEffect/useLayoutEffect 中,直接使用 DOM/BOM 属性

2.在非 useEffect/useLayoutEffect 使用 DOM/BOM 属性时,使用 isBrowser 判断是否在浏览器环境执行

3.如果某个 Hooks 需要接收 DOM/BOM 属性,需要支持函数形式传参。以 ahooks 的 useEventListener 举例,必须支持函数形式来指定 target 属性。

  1. import React, { useState } from 'react'
  2. import { useEventListener } from 'ahooks'
  3.  
  4. export default () => { 
  5.   const [value, setValue] = useState(0); 
  6.  
  7.   const clickHandler = () => { 
  8.     setValue(value + 1); 
  9.   }; 
  10.  
  11.   useEventListener( 
  12.     'click',  
  13.     clickHandler,  
  14.     {  
  15. -       target: document.getElemenetById('click-btn')  
  16. +       target: () => document.getElemenetById('click-btn')  
  17.     } 
  18.   ); 
  19.  
  20.   return ( 
  21.     <button id="click-btn" type="button"
  22.       You click {value} times 
  23.     </button> 
  24.   ); 
  25. }; 

4.使用 useIsomorphicLayoutEffect 来代替 useLayoutEffect

参考资料

  • fix: useDocumentVisiblility support SSR[6]
  • UmiJS 服务端渲染[7]
  • useLayoutEffect and SSR[8]

参考资料

[1]服务端渲染(SSR):

https://umijs.org/zh-CN/docs/SSR#服务端渲染(SSR)

[2]isBrowser:

https://github.com/alibaba/hooks/blob/master/packages/hooks/src/utils/canUseDom.ts

[3]react-redux:

https://github.com/reduxjs/react-redux/blob/d16262582b2eeb62c05313fca3eb59dc0b395955/src/components/connectAdvanced.js#L40

[4]react-use:

https://github.com/streamich/react-use/blob/master/src/useIsomorphicLayoutEffect.ts

[5]react-beautiful-dnd:

https://github.com/atlassian/react-beautiful-dnd/blob/master/src/view/use-isomorphic-layout-effect.js

[6]fix: useDocumentVisiblility support SSR:

https://github.com/alibaba/hooks/pull/935/files

[7]UmiJS 服务端渲染:

https://umijs.org/zh-CN/docs/SSR#window-is-not-defined-document-is-not-defined-navigator-is-not-defined

[8]useLayoutEffect and SSR:

https://medium.com/@alexandereardon/uselayouteffect-and-SSR-192986cdcf7a

本文转载自微信公众号「前端技术砖家」,可以通过以下二维码关注。转载本文请联系前端技术砖家公众号。

 

责任编辑:姜华 来源: 前端技术砖家
相关推荐

2019-10-08 16:05:19

Redis数据库系统

2019-04-04 13:11:37

React内存泄露memory leak

2010-08-31 16:09:04

DIV+CSS

2010-09-01 14:51:12

CSSIEFirefox

2016-09-27 21:14:53

JavaURL

2011-07-26 16:05:19

Oracle数据库服务器

2014-01-07 13:54:02

HadoopYARN

2021-08-05 08:32:27

React开发项目

2010-08-04 10:20:30

Flex组件开发

2023-04-12 11:32:33

网络

2010-08-26 12:59:29

marginCSS

2021-08-20 15:49:13

电脑主板维修

2009-12-24 11:13:41

2011-05-06 17:25:58

硒鼓

2010-09-07 13:24:18

CSS

2009-08-24 10:37:11

Silverlight

2010-08-31 13:49:12

CSS

2011-02-22 14:00:16

vsftpd

2020-12-15 12:43:53

Nodenode应用NodeJS

2022-03-26 13:31:18

项目node变量
点赞
收藏

51CTO技术栈公众号