简体   繁体   English

为什么组件在 Click 和 state 上会重新渲染?

[英]Why does Component re-renders on Click and state change?

Why does a whole component in react re-render when you change state in a onClick?为什么当您在 onClick 中更改 state 时,反应中的整个组件会重新呈现?

exmaple:https://codesandbox.io/s/vibrant-firefly-sgk5g?file=/src/App.js 示例:https://codesandbox.io/s/vibrant-firefly-sgk5g?file=/src/App.js

When you click on the numbers the whole components re-renders, and if you remove the setCount from the on click function it works just fine当您单击数字时,整个组件会重新呈现,并且如果您从单击 function 中删除 setCount,它就可以正常工作

The idea behind the component is to add a "Active" class to the number that you have clicked, and it updated a random counter, that counter prevents the addition the "active" class, since it re-renders the whole component该组件背后的想法是将“活动” class 添加到您单击的数字中,并且它更新了一个随机计数器,该计数器阻止添加“活动” class,因为它重新渲染了整个组件

EDIT: code here aswell编辑:这里也有代码

import React, { useState } from "react";

const Hours = () => {
  const days = [1, 2, 3, 4, 5, 6];
  const [count, setCount] = useState(1);

  const TestClick = (e, item) => {
    setCount(count + 1);
    e.currentTarget.className = "active";
  };

  const HandleHours = () => {
    let block = <span />;
    if (days) {
      block = days.map((hour, index) => {
        return (
          <span
            style={{ display: "block" }}
            onClick={e => {
              TestClick(e, hour);
            }}
            className={`col-md-4`} key={index}>
            {hour}
          </span>
        );
      });
    }
    return block;
  };

  return (
    <div>
      <HandleHours />
    </div>
  );
};

export default Hours;

It's the way react rerenders when a component state changes.这是当组件 state 发生变化时重新渲染的方式。 The state hook rerenders the whole component that it's in when the setState function is called which is the second element in the array that useState returns. state 钩子在调用 setState function 时重新呈现它所在的整个组件,这是 useState 返回的数组中的第二个元素。

If you want to change the class of an element on click, you need to store it as a state.如果要在单击时更改元素的 class,则需要将其存储为 state。 In your code, the class of clicked span is updated on click, but right after that the component is rerendered and set to what the HandleHours returns.在您的代码中,单击跨度的 class 会在单击时更新,但之后组件会重新呈现并设置为 HandleHours 返回的内容。

I would probalby have a state that keeps track which day is clicked and render that accordingly (not sure why you need the count, but I left it there):我很可能有一个 state 来跟踪点击了哪一天并相应地呈现(不知道为什么你需要计数,但我把它留在那里):

import React, { useState } from "react";
const Hours = () => {
  const days = [1, 2, 3, 4, 5, 6];
  const [count, setCount] = useState(1);
  const [clickedDays, setClickedDays] = useState([]); // Added clickedDays state

  const TestClick = (e, item, isDayClicked) => {
    setCount(count + 1);
    if (!isDayClicked) { // Setting clicked days if they are not in the array yet
      setClickedDays([...clickedDays, item])
    }
  };

  const HandleHours = () => {
    let block = <span />;
    if (days) {
      block = days.map((hour, index) => {
        const isDayClicked = clickedDays.includes(hour);
        return (
          <span
            style={{ display: "block" }}
            onClick={e => {
              TestClick(e, hour, isDayClicked);
            }}
            className={isDayClicked ? 'active' : 'col-md-4'} // Setting different class depending on state
            key={index}
          >
            {hour}
          </span>
        );
      });
    }
    return block;
  };

  return (
    <div>
      <HandleHours />
    </div>
  );
};

export default Hours;

The issue here isn't coming from the fact that the HandleHours components render but because it gets remounted everytime you change the state in the Hours component.这里的问题不是来自 HandleHours 组件呈现的事实,而是因为每次更改 Hours 组件中的 state 时都会重新安装它。

This happens because HandleHours is defined as a component within Hours component and everytime Hours re-renders a new reference to HandleHours is created which fools react into thinking that the component detached from DOM and a new component replaces it, since it essentialy works on reference.发生这种情况是因为HandleHours被定义为Hours组件中的一个组件,并且每次Hours重新呈现一个对HandleHours的新引用时,都会创建一个傻瓜的反应,认为该组件与 DOM 分离并且一个新组件替换它,因为它本质上是在引用上工作的。

Now when you render HandleHours like现在,当您渲染 HandleHours 时

<div>
  { HandleHours () }
</div>

Suddenly HandleHours turns from being a component to a function which returns JSX so this time when the Hours component re-renders, even though the function reference to HandleHours has changed.突然, HandleHours从一个组件变成了返回 JSX 的 function,所以这一次当Hours组件重新渲染时,即使 function 对HandleHours的引用已经改变。 It returns the JSX with a key prop on it, which remains the same and hence React treats it as a re-render and hour changes to DOM elements aren't lost它返回带有 key prop 的 JSX,它保持不变,因此 React 将其视为重新渲染,并且对 DOM 元素的小时更改不会丢失


Now there is a solution to the first approach too现在也有第一种方法的解决方案

All you need to do is to create a component HandleHours outside of your Hours component and render it by passing the required props like您需要做的就是在您的Hours组件之外创建一个组件HandleHours并通过传递所需的道具来呈现它,例如

import React, { useState } from "react";
import "./styles.css";

const HandleHours = ({ days, TestClick }) => {
  let block = <span />;
  if (days) {
    block = days.map((hour, index) => {
      return (
        <span
          style={{ display: "block" }}
          onClick={e => {
            TestClick(e, hour);
          }}
          className={`col-md-4`}
          key={index}
        >
          {hour}
        </span>
      );
    });
  }
  return block;
};

const days = [1, 2, 3, 4, 5, 6];
const Hours = () => {
  const [count, setCount] = useState(1);

  const TestClick = (e, item) => {
    setCount(count + 1);
    console.log("TestClick");
    e.currentTarget.className = "active";
  };

  return (
    <div>
      <HandleHours days={days} TestClick={TestClick} />
    </div>
  );
};

export default Hours;

When you do that the HandleHours component isn't re-mounted on each rerender of Hours component and it maintains the DOM elements correctly.当您这样做时, HandleHours组件不会重新安装在Hours组件的每次重新渲染上,它会正确维护 DOM 元素。

Here is a working demo for the second approach 这是第二种方法的工作演示

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

相关问题 组件在状态更改时重新渲染 - Component re-renders wierdly on state change 反应子组件重新渲染而不改变状态 - React child component Re-renders without state change 组件没有根据状态变化重新呈现吗? - Component didn't re-renders on state change? 将重新呈现限制为功能组件的特定状态更改 - Restricting re-renders to specific state change in functional component React 功能组件随着数组状态的变化呈指数级重新渲染 - React Functional Component Re-renders exponentially with array state change 为什么我的卡片列表组件在每次更改时都会重新渲染 - Why does my Card List Component Re-renders on every change React:父组件重新渲染所有子组件,即使是那些在状态改变时没有改变的子组件 - React: Parent component re-renders all children, even those that haven't changed on state change 你能控制基于 state 变化的功能性反应组件的重新渲染吗? - Can you control the re-renders of a functional react component based on state change? 为什么使用状态管理器像 Zustand 组件仍然重新渲染? - Why using state manager like Zustand component still re-renders? 为什么即使更改了 prop 或 state 与 JSX 无关,React 也会重新渲染组件? - Why React re-renders a component even when changed prop or state has no relation to JSX?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM