簡體   English   中英

React:停止映射組件重新渲染

[英]React: Stop mapped component re-rendering

我有一個無限滾動器,它一次調用一個組件<Goat />五個,每次都給它隨機屬性,比如名稱和圖像。 向底部滾動並加載另外 5 個。當顯示新的 5 個時,我已經能夠防止現有組件重新渲染:

shouldComponentUpdate(nextProps){
    return false;
}

這工作正常,但如果我要將唯一鍵傳遞給列表中的每個鍵,它會再次重新呈現頁面上所有現有的<Goat />組件。

我可以在傳遞密鑰時防止這種情況發生嗎?

應用程序.js:

import React from "react";
import Goat from "./components/addGoat";
import "./App.css";
import Toggle from "./components/toggle";
import InfiniteScroll from "react-infinite-scroll-component";
import uuid from "uuid";
import Header from "./components/Header";
import Theme from "./components/Theme";

class App extends React.Component {
  state = {
    items: Array.from({ length: 5 }),
    darkMode: false
  };

  fetchMoreData = () => {
    this.setState({
      items: this.state.items.concat(Array.from({ length: 5 }))
    });
  };

  getStyle = () => {
    return {
      background: this.state.darkMode ? "#464646" : "#eee",
      transition: "all 0.4s ease",
      color: this.state.darkMode ? "#eee" : "#464646"
    };
  };

  themeToggle = () => {
    console.log("changing style");
    this.setState({
      darkMode: !this.state.darkMode
    });
    this.getStyle();
  };

  render() {
    return (
      <div className="App" style={this.getStyle()}>
        <Theme theme={this.themeToggle} />
        <Header />
        <Toggle>
          <InfiniteScroll
            dataLength={this.state.items.length}
            next={this.fetchMoreData}
            hasMore={true}
            loader={<h4>Loading...</h4>}
            style={this.getStyle()}
          >
            {this.state.items.map((i, index) => (
              <Goat key={uuid.v4()} />
            ))}
          </InfiniteScroll>
        </Toggle>
      </div>
    );
  }
}

export default App;

山羊:

import React, { Component } from "react";

class Goat extends Component {
  state = {
    usedFirstNames: []
  };

  shouldComponentUpdate(nextProps) {
    return false;
  }

  render() {
    const arFirstNames = ["Napoleon", "Albert", "Phil"];

    const arLastNames = ["Jones", "Da Goat", "Williams"];

    const arStoryStart = [
      "Born in hell, this goat is not to be reckoned with.",
      "One of 16 siblings, this goat craves attention.",
      "This French animal loves baguettes... and anything else edible actually."
    ];

    const arStoryMid = [
      "Once tipped off as the next big thing in Winter Sports.",
      "The country people believe that this goat can backflip on command.",
      "Serial eater of buttered baguettes."
    ];

    const arStoryEnd = [
      "This boy has had several medicals and all doctors believe that he will live forever.",
      "Has been warned on numerous occasions that he must stop eating double cheese pizzas before his heart gives out."
    ];

    var rand = Math.floor(Math.random() * 100 + 1);
    var newFirstName = this.state.usedFirstNames.slice();
    let firstName = [];

    do {
      firstName = arFirstNames[Math.floor(Math.random() * arFirstNames.length)];
      this.setState({
        usedFirstNames: [...this.state.usedFirstNames, firstName]
      });
    } while (this.state.usedFirstNames.includes(newFirstName));
    // Fix this buy passing props to app js rather than setting state in this component

    let lastName = arLastNames[Math.floor(Math.random() * arLastNames.length)];
    let randomStory =
      arStoryStart[Math.floor(Math.random() * arStoryStart.length)] +
      " " +
      arStoryMid[Math.floor(Math.random() * arStoryMid.length)] +
      " " +
      arStoryEnd[Math.floor(Math.random() * arStoryEnd.length)];

    //Add check here to see if firstName and/or last name has existing in the previous iterations
    //array.includes array.shift array.push

    let randomName = firstName + " " + lastName;

    return (
      <div className="GoatItem" id="Goats" onScroll={this.handleScroll}>
        <img
          className="GoatImg"
          src={"/images/goats/" + rand + ".jpg"}
          alt="goat"
        />
        <div className="GoatContent">
          <p>{randomName}</p>
          <div className="GoatStory">
            <p>{randomStory}</p>
          </div>
        </div>
      </div>
    );
  }
}

export default Goat;

當前沒有密鑰的演示 - https://lukes-code-infinitescroats.netlify.com

提前致謝。

問題:每次調用uuid都會生成一個唯一的字符串。 因為您在 render 函數中調用它,它會強制為所有映射的組件提供一個新鍵。

文檔說:

鍵幫助 React 識別哪些項目已更改、添加或刪除。 應為數組內的元素提供鍵,以賦予元素穩定的標識。

所以你不應該在每次父組件發生任何變化時都給他們一個新的身份。

這個 Codesandbox 正好展示了這一點。 你必須在你的開發工具中啟用paint falshing來檢查重新渲染。

解決方案:不要在 render 函數中使用uuid ,相反,如果您可以創建一個對象數組,或者如果您已經有一個對象,請創建一個鍵來保留該子對象的 id。 像這樣[{...item, id: uuid() }, ...]

當您傳遞唯一的鍵時,React 不會將它們與渲染的原始組件進行匹配。 因此,即使使用shouldComponentUpdate: () => false你仍然會渲染,因為它們是新組件,而不是正在更新的組件。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM