如果部分状态发生变化,如何停止反应重新渲染组件? [英] How to stop react re-rendering component, if part of the state changes?

查看:67
本文介绍了如果部分状态发生变化,如何停止反应重新渲染组件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果只有部分状态改变,有没有办法停止反应重新渲染?

问题是,每次我将鼠标悬停在标记上时,都会打开或关闭一个弹出窗口,即使 mystate 不只更改 activePlace状态正在改变.console.log(myState); 每次我将鼠标悬停在标记内外时都会运行.

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

这是我的代码:

import React, { useEffect, useState } from 'react';从'react-leaflet'导入{ Map, TileLayer, Marker, Popup };从 'axios' 导入 axios;从 'uuid' 导入 { v4 as uuidv4 };从传单"导入{图标};const myicon = 新图标({iconUrl: './icon.svg',图标大小:[20, 20]});const MyMap = () =>{const [myState, setMyState] = useState(null);const [activePlace, setActivePlace] = useState(null);const getData = async() =>{让响应 = 等待 axios.get('https://corona.lmao.ninja/v2/jhucsse').catch(err => console.log(err));让数据 = response.data;设置我的状态(数据);//console.log(data);};useEffect(() => {获取数据();}, []);如果(我的状态){控制台日志(我的状态);返回 (<地图样式={{ 高度:'100vh',宽度:'100vw' }}中心={[14.561, 17.102]}缩放={1}><瓷砖层归因='&copy;<a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>贡献者&copy;<a href="https://carto.com/attributions">CARTO</a>'网址={'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png'}/>{myState.map(country => {返回 (<标记键={uuidv4()}位置={[国家.坐标.纬度,国家.坐标.经度]}onmouseover={() =>{setActivePlace(国家);}}onmouseout={() =>{setActivePlace(null);}}图标={myicon}/>);})}{activePlace &&(<弹出窗口位置={[activePlace.coordinates.latitude,activePlace.coordinates.longitude]}><div><h4>国家:{activePlace.country}</h4>

</弹出窗口>)}</地图>);} 别的 {返回<div>加载</div>;}};导出默认的 MyMap;

解决方案

这一行是你的问题:

key={uuidv4()}

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

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

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

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

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

这意味着当 activePlace 更改时,整个组件运行其渲染周期完全没问题.

但是,由于您在 每个 渲染周期中为每个 country 赋予一个全新的 ID,因此虚拟 DOM 认为 每个 country已更改(它使用 ID 来比较以前的 DOM 值),因此实际 DOM 中的所有国家/地区也会重新渲染,这可能就是您看到滞后问题的原因.

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

Is there a way to stop react re-rendering if only part of state changes?

The problem is that every time I hover on a marker a popup is opened or closed and it causes all the markers to re-render even though mystate is not changing only activePlace state is changing. console.log(myState); is running every time I hover in and out of the marker.

I tried to use useMemo hook but couldn't figure out how to use it. Any help?

Here is my code:

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;

解决方案

This line is your problem:

key={uuidv4()}

Why are you creating a unique ID on every render? The point of an ID is that it stays the same between renders so that React knows that it doesn't have to re-draw that component in the DOM.

There are two stages that happen whenever state changes, the render phase and the commit phase.

The render phase happens first, and this is where all of your components execute their render functions (which is the entire component in the case of a function component). The JSX that is returned is turned into DOM nodes and added to the virtual DOM. This is very efficient and is NOT the same as re-rendering the actual DOM.

In the commit phase, the new virtual DOM is compared to the real DOM, and any differences found in the real DOM will be re-rendered.

The point of React is to limit the re-renders of the real DOM. It is not to limit the recalculation of the virtual DOM.

This means that it is totally fine fine for this entire component to run its render cycle when activePlace changes.

However, because you're giving a brand new ID to each country on every render cycle, the virtual DOM thinks that every country has changed (it uses IDs to compare previous DOM values), so all the countries in the actual DOM also get re-rendered, which is probably why you're seeing issues with lag.

The ID should be something related to the country, e.g. a country code, not just a random UUID. If you do use random UUIDs, save them with the country so that the same one is always used.

这篇关于如果部分状态发生变化,如何停止反应重新渲染组件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆