Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
582 views
in Technique[技术] by (71.8m points)

javascript - 反应:停止映射组件的重新渲染(React: Stop mapped component re-rendering)

I have an infinite scroller that calls on a component <Goat /> five at a time giving it random attributes like name and image each time.

(我有一个无限滚动器,每次调用一个组件<Goat /> 5个,每次给它一个随机属性,例如name和image。)

The scroll towards the bottom and another 5 are loaded in. I have been able to prevent the existing components from re-rendering when the new 5 are displayed with:

(向下滚动并加载了另外5个滚动条。当显示新的5个滚动条时,我已经能够防止重新渲染现有组件:)

shouldComponentUpdate(nextProps){
    return false;
}

This works fine but if I am to pass a unique key to each in the list, it is once again re-rendering all existing <Goat /> components on the page.

(这可以正常工作,但是如果我要将唯一的密钥传递给列表中的每个密钥,它将再次重新呈现页面上所有现有的<Goat />组件。)

Can I prevent this from happening when passing the keys?

(传递密钥时是否可以防止这种情况发生?)

App.js:

(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;

Goat:

(山羊:)

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;

Current demo without keys - https://lukes-code-infinitescroats.netlify.com

(当前没有按键的演示-https: //lukes-code-infinitescroats.netlify.com)

Thanks in advance.

(提前致谢。)

  ask by lukes.code translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Problem: uuid generates a unique string every time called.

(问题: uuid每次调用都会生成一个唯一的字符串。)

Because you're calling it inside render function, which forces all the mapped components to be fed with a new key.

(因为您是在render函数中调用它的,所以它将强制为所有映射的组件提供新的键。)

Docs Says:

(Docs说:)

Keys help React identify which items have changed, are added, or are removed.

(按键可帮助React识别哪些项目已更改,添加或删除。)

Keys should be given to the elements inside the array to give the elements a stable identity.

(应该为数组内的元素提供键,以赋予元素稳定的身份。)

So you should not give them a new identity every time anything in the parent component changes.

(因此,每次父组件中的任何内容发生更改时,都不应给它们一个新的标识。)

This Codesandbox shows this exactly.

(此Codesandbox准确显示了这一点。)

You have to enable paint falshing in your dev tool to check the rerenders.

(您必须在开发工具中启用涂漆检查,以检查重新渲染。)

Solution: Don't use uuid inside render function, instead if you can create an array of objects or if you already have one, create a key which will keep the id for that child.

(解决方案:不要在渲染函数中使用uuid ,而是如果您可以创建对象数组或已经有一个对象数组,则创建一个将保留该孩子ID的键。)

Something like this [{ ...item, id: uuid() }, ...] .

(这样的东西[{ ...item, id: uuid() }, ...] 。)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...