簡體   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