繁体   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