反应:停止映射组件的重新渲染

时间:2019-11-30 10:39:35

标签: javascript reactjs

我有一个无限滚动器,它一次调用五个组件<Goat />,每次给它一个随机属性,例如名称和图像。 往底部滚动并加载另外5个滚动条。 当新的5显示为:

时,我已经能够防止重新渲染现有组件。
shouldComponentUpdate(nextProps){
    return false;
}

这很好用,但是如果我要将唯一的密钥传递给列表中的每个密钥,它将再次重新呈现页面上所有现有的<Goat />组件。

我可以防止在传递密钥时发生这种情况吗?

App.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

谢谢。

2 个答案:

答案 0 :(得分:1)

问题: 每次调用uuid都会生成一个唯一的字符串。因为您是在render函数中调用它的,所以它会强制为所有映射的组件提供新的键。

  

Docs说:

     

键可帮助React识别哪些项目已更改,添加或删除。应该为数组内的元素赋予键,以赋予元素稳定的身份。

因此,每次父组件中的任何内容发生更改时,您都不应给他们一个新的身份。

This Codesandbox准确地显示了这一点。您必须在开发工具中启用paint falshing才能检查转投。

解决方案:请勿在渲染函数中使用uuid,而是如果您可以创建对象数组或已经拥有对象数组,则创建一个密钥以保留ID那个孩子像这样的[{ ...item, id: uuid() }, ...]

答案 1 :(得分:0)

当传递唯一的键时,react不会将它们与呈现的原始组件匹配。因此,即使使用shouldComponentUpdate: () => false,您仍将进行渲染,因为它们是新组件,而不是要更新的组件。