【问题标题】:How to stop react re-rendering component, if part of the state changes?如果部分状态发生变化,如何停止反应重新渲染组件?
【发布时间】:2020-07-09 13:41:02
【问题描述】:

如果只有部分状态发生变化,有没有办法停止响应重新渲染?

问题在于,每次我将鼠标悬停在标记上时,都会打开或关闭一个弹出窗口,这会导致所有标记重新呈现,即使 mystate 没有改变,只是 activePlace 状态正在改变。 console.log(myState); 每次我在标记内外悬停时都会运行。

我尝试使用 useMemo 钩子,但不知道如何使用它。有什么帮助吗?

这是我的代码:

import React, { useEffect, useState } from 'react';
import { Map, TileLayer, Marker, Popup } from 'react-leaflet';
import axios from 'axios';
import { v4 as uuidv4 } from 'uuid';
import { Icon } from 'leaflet';

const myicon = new Icon({
  iconUrl: './icon.svg',
  iconSize: [20, 20]
});

const MyMap = () => {
  const [myState, setMyState] = useState(null);
  const [activePlace, setActivePlace] = useState(null);

  const getData = async () => {
    let response = await axios
      .get('https://corona.lmao.ninja/v2/jhucsse')
      .catch(err => console.log(err));

    let data = response.data;
    setMyState(data);

    // console.log(data);
  };

  useEffect(() => {
    getData();
  }, []);

  if (myState) {
    console.log(myState);
    return (
        <Map
          style={{ height: '100vh', width: '100vw' }}
          center={[14.561, 17.102]}
          zoom={1}
        >
          <TileLayer
            attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="https://carto.com/attributions">CARTO</a>'
            url={
              'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png'
            }
          />

          {myState.map(country => {
            return (
              <Marker
                key={uuidv4()}
                position={[
                  country.coordinates.latitude,
                  country.coordinates.longitude
                ]}
                onmouseover={() => {
                  setActivePlace(country);
                }}
                onmouseout={() => {
                  setActivePlace(null);
                }}
                icon={myicon}
              />
            );
          })}

          {activePlace && (
            <Popup
              position={[
                activePlace.coordinates.latitude,
                activePlace.coordinates.longitude
              ]}
            >
              <div>
                <h4>Country: {activePlace.country}</h4>
              </div>
            </Popup>
          )}
        </Map>
    );
  } else {
    return <div>Loading</div>;
  }
};

export default MyMap;

【问题讨论】:

  • 您是否已经尝试过使用 Pure Component 或者您需要使用无状态组件来保留 react hooks?
  • @VictorAlessander 我没有尝试过纯组件。我不明白你说的下一部分。
  • 无状态组件也称为函数组件(reactjs.org/docs/…
  • @VictorAlessander 有没有办法修复它,同时保持它是一个功能组件,我不想使用基于类的组件
  • 仅仅因为这个函数在状态改变时重新运行,并不意味着 UI 正在重新渲染。这是否会导致您的应用程序出现问题,或者您只是担心因为您认为它可能会这样做?

标签: javascript reactjs react-hooks react-component react-state-management


【解决方案1】:

这一行是你的问题:

key={uuidv4()}

为什么要在每次渲染时创建一个唯一 ID? ID 的意义在于它在渲染之间保持不变,以便 React 知道它不必在 DOM 中重新绘制该组件。

每当状态发生变化时,都会发生两个阶段,渲染阶段提交阶段

渲染阶段首先发生,这是所有组件执行其渲染功能的地方(在功能组件的情况下是整个组件)。返回的 JSX 变成 DOM 节点并添加到 virtual DOM。这非常有效,并且与重新渲染实际 DOM 不同。

在提交阶段,将新的虚拟 DOM 与真实 DOM 进行比较,在真实 DOM 中发现的任何差异都会重新渲染。

React 的重点是限制真实 DOM 的重新渲染。不是限制虚拟DOM的重新计算。

这意味着当activePlace 发生变化时,整个组件运行其渲染周期是完全没问题的。

但是,由于您在 每个 渲染周期为每个 country 提供了一个全新的 ID,因此虚拟 DOM 认为 每个 国家/地区都发生了变化(它使用用于比较之前 DOM 值的 ID),因此实际 DOM 中的所有国家/地区也会重新渲染,这可能就是您看到延迟问题的原因。

ID 应该是与国家/地区相关的内容,例如国家代码,而不仅仅是随机的 UUID。如果您确实使用了随机 UUID,请将它们与国家/地区一起保存,以便始终使用相同的 UUID。

【讨论】:

  • 这很有意义。太感谢了!!!我的地图现在非常平滑,标记的闪烁也停止了。你能告诉我最后一件事,为什么每次弹出窗口打开/关闭时console.log(myState) 仍在运行?
  • 每次渲染阶段发生时都会运行console.log,并且每次组件状态发生变化时都会发生渲染阶段。没什么好担心的。这是预期的行为。
  • 哦,好的。再次感谢你,这些标记把我吓坏了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-04
  • 1970-01-01
  • 1970-01-01
  • 2022-11-07
  • 2021-02-22
相关资源
最近更新 更多