简体   繁体   English

React 钩子会导致无意的重新渲染

[英]React hooks cause unintentional re-render

I am working with a React + Leaflet map and noticing the map container / elements re-render any time I show or close a modal.我正在使用 React + Leaflet map 并注意到 map 容器/元素在我显示或关闭模式时重新渲染。

I have created a sandbox to demonstrate the problem, but this occurs in other places throughout my app if additional examples would help.我创建了一个沙箱来演示该问题,但如果其他示例有帮助,这会在我的应用程序中的其他地方发生。

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

From reading alternative SO posts, such as this very similar one , I recognize this is likely caused by my hooks (ex. handleShow/setShow) which force the entire component to re-render.通过阅读替代 SO 帖子,例如这个非常相似的帖子,我认识到这可能是由我的钩子(例如 handleShow/setShow)引起的,它强制整个组件重新渲染。 The unintended behavior is noticed as follows:意外行为如下所示:

If you drag the map so that the current location is out of view and open a modal, we force the re-load of <LeafletControlGeocoder/> and <LocationMarker/> .如果拖动 map 使当前位置不在视野范围内并打开模态框,我们会强制重新加载<LeafletControlGeocoder/><LocationMarker/> This is evident because the map re-pans to the current location, and a new 'search icon' appears in the top right on the map.这很明显,因为 map 会重新平移到当前位置,并且 map 的右上角会出现一个新的“搜索图标”。

Steps to replicate:复制步骤:

*If you notice an issue in sandbox related to react-bootstrap/Modal, just update the dependency to latest (refresh icon) - this is a weird sandbox issue but unrelated to my question. *如果您在沙箱中发现与 react-bootstrap/Modal 相关的问题,只需将依赖项更新为最新(刷新图标) - 这是一个奇怪的沙箱问题,但与我的问题无关。

  1. Drag map so that current location is out of view拖动 map 使当前位置不在视野范围内
  2. Click menu icon (top right) > Add Location单击菜单图标(右上角)> 添加位置
  3. When modal appears, notice map re-centers to current location and another search icon appears.当模态出现时,请注意 map 重新居中到当前位置并出现另一个搜索图标。

The issue is caused by having some child components function defined within the body of another React functional component.该问题是由于在另一个 React 功能组件的主体中定义了一些子组件 function 引起的。

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>
    </>
  );
}

This may not be problematic if your child components are stateless (no hook).如果您的子组件是无状态的(没有挂钩),这可能不会有问题。 In that case they are more like "sub-templates".在这种情况下,它们更像是“子模板”。

But if those child components do use some hooks, then being in the body of another functional component will interfere with how the hooks work: because the child component function is re-created at each re-render, React has trouble identifying those children as being the same component, and ends up duplicating their output.但是如果这些子组件确实使用了一些钩子,那么在另一个功能组件的主体中会干扰钩子的工作方式:因为子组件 function 在每次重新渲染时都会重新创建,React 无法识别这些子组件是相同的组件,最终复制了他们的 output。

Simply make sure not to define some Functional Components within another one, but always at the top-level of your scope (typically at the root of your file).只需确保不要在另一个中定义一些功能组件,而是始终在 scope 的顶层(通常在文件的根目录)。 As mentioned in the question comments, a standard practice is simply to have 1 component per file.如问题评论中所述,标准做法是每个文件只有一个组件。

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.
}

Fixed app: https://codesandbox.io/s/lively-monad-yve67固定应用程序: https://codesandbox.io/s/lively-monad-yve67

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

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