React hooks 导致无意的重新渲染

React hooks cause unintentional re-render

我正在使用 React + Leaflet 地图并注意到地图容器/元素在我显示或关闭模式时重新渲染。

我已经创建了一个沙箱来演示该问题,但如果其他示例会有所帮助,我的应用程序中的其他地方也会出现这种情况。

https://codesandbox.io/s/elegant-rain-01f9l?file=/src/App.js

通过阅读其他 SO 帖子,例如 ,我认识到这可能是由我的钩子(例如 handleShow/setShow)引起的,它强制整个组件重新呈现。意外行为如下所示:

如果拖动地图使当前位置超出视野并打开模式,我们会强制重新加载 <LeafletControlGeocoder/><LocationMarker/>。这是显而易见的,因为地图重新平移到当前位置,并且新的 'search icon' 出现在地图的右上角。

复制步骤:

*如果您注意到沙箱中与 react-bootstrap/Modal 相关的问题,只需将依赖项更新为最新(刷新图标)——这是一个奇怪的沙箱问题,但与我的问题无关。

  1. 拖动地图使当前位置不在视野范围内
  2. 单击菜单图标(右上角)> 添加位置
  3. 当模式出现时,通知地图重新居中到当前位置并出现另一个搜索图标。

此问题是由于在另一个 React 功能组件的主体 中定义了一些 child 组件功能

function App() { // A React functional component
  const [show, setShow] = useState(false);
  // etc.

  // A child React functional component in the body of another component...
  function LocationMarker() {
    const [position, setPosition] = useState(null);
    // etc.
  }

  // Another child component
  function LeafletControlGeocoder() {
    useEffect(() => {}, []);
    // etc.
  }

  return (
    <>
      {/* More content... */}
      <MapContainer center={[45.4, -75.7]} zoom={12}>
        <LeafletControlGeocoder />
        <LocationMarker />
      </MapContainer>
    </>
  );
}

如果您的 child 组件是无状态的(没有钩子),这可能不会有问题。在那种情况下,它们更像是“sub-templates”。

但是如果那些 child 组件确实使用了一些钩子,那么在另一个功能组件的主体中将干扰钩子的工作方式:因为 child 组件函数是 re-created 在每个 re-render,React 都无法将这些 children 识别为相同的组件,并最终复制它们的输出。

只需确保不要在另一个组件中定义某些功能组件,而是始终在您的范围的 top-level 中(通常在文件的根目录中)。如问题评论中所述,标准做法是每个文件仅包含 1 个组件。

function App() { // A React functional component
  const [show, setShow] = useState(false);
  // etc.

  return (
    <>
      {/* More content... */}
      <MapContainer center={[45.4, -75.7]} zoom={12}>
        <LeafletControlGeocoder />
        <LocationMarker />
      </MapContainer>
    </>
  );
}

// Another React functional component at top scope level
function LocationMarker() {
  const [position, setPosition] = useState(null);
  // etc.
}

// Another component
function LeafletControlGeocoder() {
  useEffect(() => {}, []);
  // etc.
}

固定应用:https://codesandbox.io/s/lively-monad-yve67