【问题标题】:Preventing excessive rerendering when using mapbox and react context使用 mapbox 和 react 上下文时防止过度重新渲染
【发布时间】:2022-01-02 05:10:45
【问题描述】:

我有一个使用 mapbox 地图的应用程序,我试图允许从另一个单独的卡片组件调整视口。我这样做的方法是创建一个视口上下文并将视口和设置它的函数传递给地图和卡片组件。

这可行,但我遇到了重新渲染的问题。每当用户在地图上滚动时,视口会不断变化,因此 Viewport Provider 中的所有内容都会不断重新渲染。代码的简化版本如下

应用程序

<ViewportProvider>
   *Container that holds both the map and the sidebar that renders the card components*
</ViewportProvider>

视口提供者

import React from "react";
const INITIAL_STATE = {
  longitude: 15,
  latitude: 25,
  zoom: 1

};

export const ViewportContext = React.createContext();

export const ViewportProvider = (props) => {
  const [viewport, setViewport] = React.useState(INITIAL_STATE);


  return (
    <ViewportContext.Provider
      value={{
        viewport,
        setViewport,
      }}
      {...props}
    />
  );
}

地图

import React, { useContext } from "react";
import ReactMapGL from "react-map-gl";
import { ViewportContext } from "./ViewportProvider";

const { viewport, setViewport } = useContext(ViewportContext);

<Map onViewportChange={nextViewport => setViewport(nextViewport)} {...viewport}/>

卡片

const {setViewport} = useContext(ViewportContext)

<Card onClick={setViewport(...newValue)}/>

当用户在地图上移动时,如何在不失去用户从卡片组件调整视口的能力的情况下停止过度重新渲染?

【问题讨论】:

    标签: javascript reactjs react-hooks mapbox viewport


    【解决方案1】:

    通过将viewportsetViewport 放在同一个上下文中,您将强制侧边栏在每次视口更改时重新渲染,因为它使用useContext(ViewportContext)——即使它只使用setViewport 部分上下文。有几个选项:

    • 只需将useContext(ViewportContext) 移动到子组件中即可减少重新渲染对性能的影响,以便在更新视口时仅渲染子组件,而不是整个侧边栏。
    • 如果您只需要单向更新(侧边栏按钮可以设置地图的视口,但不需要其他组件知道用户何时在地图上移动),那么您可以从地图中移除 setViewport 调用。
    • viewportsetViewport 放在不同的上下文中。这意味着两个 createContext() 调用,但您仍将保留一个 ViewportProvider 组件来呈现两个提供程序,例如const [viewport, setViewport] = useState(); return &lt;Context1.Provider value={viewport}&gt;&lt;Context2.Provider value={setViewport}&gt; ...。然后,您的侧边栏可以useContext 仅用于它需要的上下文,并且不会在另一个更改时重新呈现。
    • 使用useContextSelector 之类的东西将const {setViewport} = useContext(ViewportContext) 替换为const setViewport = useContextSelector(ViewportContext, (ctx) =&gt; ctx.setViewport)
    • 如果您正在考虑更广泛的架构更改,React ReduxRecoil 等库可以提供可避免此问题的替代设计。

    (顺便说一句,在您编写的代码中,我建议您将传递给ViewportContext.Providervalue 包装在useMemo(() =&gt; ({viewport, setViewport}), [viewport, setViewport]) 中,这样当ViewportProvider 重新启动时它不会触发更新如果视口没有改变,则渲染。)

    【讨论】:

    • 感谢您的回复!明天我会在工作中尝试这些选项,看看我能做些什么。不幸的是,对于如此小的客户端状态,添加像 Redux 或 Recoil 这样的库并不是一个真正的选择(我们实际上只是从一个专用的状态管理库中移除)
    • 是的,我明白了,我们的项目是类似的。 useContextSelector 可能是最直接的解决方案,它可以为您提供最好的 redux 样式“选择器”和 React 上下文提供程序。我们在该库出现之前实现了custom version,但它的工作方式基本相同。
    猜你喜欢
    • 1970-01-01
    • 2023-02-26
    • 1970-01-01
    • 2019-11-23
    • 2021-05-03
    • 2021-01-23
    • 1970-01-01
    • 2019-05-29
    • 1970-01-01
    相关资源
    最近更新 更多