繁体   English   中英

如何在使用 Material UI 和 reactDOMServer 时修复“警告:useLayoutEffect 在服务器上什么都不做”?

[英]How to fix "Warning: useLayoutEffect does nothing on the server" while using Material UI and reactDOMServer?

我遇到了 ReactDOMServer 和 Material UI Theme Provider 的问题。一切正常,但我在控制台上不断收到这个烦人的错误:

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.

到目前为止,我发现的所有解决方案都涉及我必须删除 ThemeProvider,但我想知道是否没有更好的方法来解决这个问题?

代码:

  const mapIcon = useMemo(() => {
    let namePosition: MapPinProps['namePosition'] = 'bottom-start';

    if (position.lng > 120) {
      namePosition = 'bottom-end';
    }

    if (position.lng > 120 && position.lat < -80) {
      namePosition = 'top-end';
    }

    if (position.lng <= 120 && position.lat < -80) {
      namePosition = 'top-start';
    }

    const html = ReactDOMServer.renderToStaticMarkup(
      <ThemeProviders>
        <MapPin
          active={isMapBuilder ? active : !completed}
          highlight={highlightRequiredEvent}
          icon={completed && doneIcon ? doneIcon : icon}
          name={event?.type !== EVENTS_TYPE.ANIMATION || isMapBuilder ? name : ''}
          rarity={rarity}
          read={isMapBuilder || event?.type === EVENTS_TYPE.ANIMATION || read}
          interactive={isMapBuilder || event?.type !== EVENTS_TYPE.ANIMATION}
          selected={selected}
          shape={shape}
          size={iconSize}
          userSettings={user.settings}
          namePosition={namePosition}
          locked={locked && !isMapBuilder}
          isMapBuilder={isMapBuilder}
        />
      </ThemeProviders>
    );

    return new L.DivIcon({
      className: '',
      // iconAnchor,
      // popupAnchor,
      iconSize: [iconSize, iconSize],
      html: html.toString(),
    });
  }, [
    position.lng,
    position.lat,
    event?.type,
    isMapBuilder,
    active,
    completed,
    highlightRequiredEvent,
    doneIcon,
    icon,
    name,
    rarity,
    read,
    selected,
    shape,
    iconSize,
    user.settings,
    locked,
  ]);

我将尝试提供一些可以帮助解决该问题的选项。 我会在这里和那里做一些假设,让自己在某些情况下是错误的。

语境

useLayoutEffect()仅在 DOM 突变后触发 通常,服务器没有适当的方法来处理它。 此外,官方文档还提到:

如果您使用服务器渲染,请记住,在下载 JavaScript 之前,useLayoutEffect 和 useEffect 都不能运行。 这就是为什么当服务器渲染的组件包含 useLayoutEffect 时 React 会发出警告。

并提供了一种修复它的方法,但它需要访问源代码(我想你没有)。

假设

  • 您正在使用服务器端渲染在后端构建组件
  • 您还在客户端(前端)使用 React
  • 您正在使用 Leaflet for Maps(因为L.DivIcon()
  • 您无权更改<ThemeProviders />组件的源代码

可能的解决方案#1(可能是最好的解决方案?)

您可以尝试使用ReactDOMServer.renderToString()而不是ReactDOMServer.renderToStaticMarkup()

说明:假设您在服务器上使用它并在客户端上使用 React, 根据有关 ReactDOMServer.renderToStaticMarkup() 的官方文档

如果您打算在客户端使用 React 来使标记交互,请不要使用此方法。 相反,在服务器上使用renderToString ,在客户端使用ReactDOM.hydraRoot()

可能的解决方案 #2(可以访问ThemeProviders组件源代码)

您可以更改对useLayoutEffect()使用情况的检查,并阻止它在没有任何 DOM 的服务器上使用它。

说明:根据GitHub 问题中的此评论,它建议根据 DOM 的存在在useLayoutEffect()useEffect()之间进行选择。 评论提到使用类似的东西:

const canUseDOM: boolean = !!(
  typeof window !== 'undefined' &&
  typeof window.document !== 'undefined' &&
  typeof window.document.createElement !== 'undefined'
);

const useIsomorphicLayoutEffect = canUseDOM ? useLayoutEffect : useEffect;

你可以在你的组件中使用这个useIsomorphicLayoutEffect()

可能的解决方案#3(不使用 SSR)

您可以考虑删除ReactDOMServer的使用。

说明:这似乎是一个非常简单的组件,与服务器渲染相比,选择在客户端渲染它的成本可能微不足道。 注意:在选择删除 SSR 之前,您可以根据一些基准和测试做出此决定。


我希望其中一些可以帮助你!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM