繁体   English   中英

尽管有键,为什么我的所有子组件都重新渲染?

[英]Why are all my child components re-rendering despite having keys?

我在玩 react-dev-tools chrome 扩展,发现我所有的组件都在重新渲染。

应用程序.js

import React from 'react';
import './App.css';
import Header from './components/molecules/Header/Header';
// import { colorListGenerator } from './core-utils/helpers';
import ColorPalette from './components/organisms/ColorPalette/ColorPalette';

export const colorListGenerator = (n) => {
  let colorArray = []
  for(let i=0; i<n; i++) {
      let randomColor = '#'+Math.floor(Math.random()*16777215).toString(16);
      let id="id" + Math.random().toString(16).slice(2)
      console.log(typeof(id), id)
      let color = {
          id: id,
          hex: randomColor
      }
      colorArray.push(color);
  }
  return colorArray
}

const App = () => {
  const colors=colorListGenerator(10);

  return (
    <div className="App">
      <Header/>
      <ColorPalette colorPalette={colors} />
    </div>
  );
}

export default App;

调色板.js

/* eslint-disable eqeqeq */
import React from 'react';
import Color from '../../atoms/Color';
import './ColorPalette.css';

const ColorPalette = ({ colorPalette }) => {

    const [colors, setColors] = React.useState(colorPalette);

    // const handleColorClick = (event) => {
    //     const id = event.currentTarget.getAttribute('id')
    //     const index = colors.findIndex(item => item.id == id);
    //     setColors(colors.filter(item => item.id != id))
    // }

    const deleteItem = (id) => {
        setColors(colors.filter(item => item.id != id))
    }

    return (
        <div className={'colorPalette'}>
            {colors && colors.map((color, index) => {
                // const key = index
                const key = color.id
                return <Color
                key={key}
                color={color.hex}
                colorKey={key} 
                handleColorClick = {() => {deleteItem(color.id)}}
                /> })}
        </div>
    )
}

// export default React.memo(ColorPalette);
export default ColorPalette;

颜色.js

import React from 'react';
import './Color.css';
import deleteIcon from '../../../delete-icon.png'

const Color = ({ color, colorKey, handleColorClick }) => {
    return (
        <div className="color"
            style={{ backgroundColor: color }}
            // no need to mention key here
            // key={colorKey}
            id={colorKey}
            onClick={handleColorClick} >
            <p> {colorKey} </p>
            <img src={deleteIcon}
                alt={'delete'}
                className="delete"
            />
        </div>
    )
}

// export default React.memo(Color);
export default Color;

当我使用分析器检查为什么在删除单个项目后我的所有“颜色”组件都重新渲染时,它抱怨 handleColorClick 道具已更改。 我将deleteItem更改为handleColorClick,它不是箭头function,但结果是一样的。 我还传递了唯一的 ID。 有趣的是,当我通过const key = Math.random()而不是const key = color.id ,我的 Color 组件不会重新渲染。 所以它与键有关。 我想了解为什么当我将唯一 ID 作为键传递时我的组件会重新渲染。

防止 React 功能组件重新渲染的唯一方法是使用React.memo来记忆组件。 这里的记忆意味着如果组件的 props 没有改变 - 它们使用===运算符严格等价 - 那么组件的最后一次渲染 output 将被重新使用,而不是重新渲染整个组件。

但是,当您谈论 object 或函数的道具时, React.memo本身会变得很棘手 - 严格===比较检查引用相等性的值。 这意味着对于像deleteItem这样的函数,需要使用像React.useCallback这样的东西来记忆引用本身,这样它们本身就不会在渲染之间发生变化,这将导致React.memo并导致在直觉上看起来应该的情况下重新渲染不。

正如你所看到的,当你试图记录你的函数、对象、组件等时,它很快开始变得相当复杂。

真的,有什么意义呢?

你从记忆中获得的性能提升——如果它们实现的话——是微乎其微的。 这是过早优化的典型案例,有时被称为“万恶之源”,因为它是一种不必要的时间消耗,几乎没有收获,而且增加了复杂性的代价。

React 本身在其优化的生产构建中非常快,擅长解决差异,并且在大多数情况下,每秒可以重新渲染整个应用程序数十次,而不会出现任何明显的减速。 仅当您对需要解决的性能产生实际的、可衡量的影响时,您才应该开始使用诸如 memoization 之类的东西来优化您的应用程序。

简而言之,您无需担心“不必要的”重新渲染。

为了强调,我再说一遍:

不要担心“不必要的”渲染。

严重地。

PS:使用随机值作为key看起来似乎消除了不必要的重新渲染的原因是因为每次渲染组件时,它实际上都是该组件的全新实例,而不是被重新渲染的相同组件。 React 在底层使用key prop 来跟踪渲染之间的哪个组件。 如果该值不可靠,则意味着 React 每次都在渲染新组件。 您基本上是在销毁所有旧组件并从头开始重新创建它们,尽管使用相同的道具或其他任何东西,但请不要误会,它们在渲染之间不是相同的组件。 (甚至包括挂钩在内的内部 state 也会被擦除)

根据您所说, handleColorClick道具已更改,这就是重新渲染组件的原因。 由于您在组件中使用功能组件和钩子,因此当组件重新渲染时,function handleColorClick将再次重新定义,并且引用正在更改。 这就是为什么即使您将唯一 ID 作为键传递,组件也会重新渲染的原因。

为了避免这种情况,您可以使用useCallback挂钩,这将帮助您不获取新的 function 引用,除非提供给useCallback挂钩的依赖项发生变化

/* eslint-disable eqeqeq */
import React, {useCallback} from 'react';
import Color from '../../atoms/Color';
import './ColorPalette.css';

const ColorPalette = ({ colorPalette }) => {

    const [colors, setColors] = React.useState(colorPalette);

    // const handleColorClick = (event) => {
    //     const id = event.currentTarget.getAttribute('id')
    //     const index = colors.findIndex(item => item.id == id);
    //     setColors(colors.filter(item => item.id != id))
    // }

    const deleteItem = useCallback((id) => {
        setColors(colors.filter(item => item.id != id))
    }, [])

    return (
        <div className={'colorPalette'}>
            {colors && colors.map((color, index) => {
                // const key = index
                const key = color.id
                return <Color
                key={key}
                color={color.hex}
                colorKey={key} 
                handleColorClick = {() => {deleteItem(color.id)}}
                /> })}
        </div>
    )
}

// export default React.memo(ColorPalette);
export default ColorPalette;

暂无
暂无

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

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