React 组件表在更新状态后被多次渲染或复制

Posted

技术标签:

【中文标题】React 组件表在更新状态后被多次渲染或复制【英文标题】:React component table is being rendered multiple times or duplicated after updating state 【发布时间】:2019-06-13 16:45:00 【问题描述】:

我正在通过替换旧状态来使用道具更改来更新状态。在渲染中,数据要么被复制,要么在使用新道具重置后从状态中多次渲染。它发生在第一次渲染之后。

这是应用程序的工作流程 - 当用户搜索位置时,首先发生获取。它在用户位置周围获得“推荐地点”。它将数据作为“推荐位置”放入 redux 存储中。有问题的组件呈现来自商店 [第一张图片] 的数据,这很好。当点击过滤器按钮时,这次它会获取用户位置和过滤器,并将数据放入redux存储中,例如,restaurants=[],bars=[]等。组件应该从存储中获取新数据并重新渲染。这就是我的问题所在。重新渲染时,它似乎重复了数据 [第二张图片]。

这是有问题的组件-

import React,  Component  from "react";
import PropTypes from "prop-types";

export default class Results extends Component 
  state = 
    places: []
  ;

getSnapshotBeforeUpdate(prevProps) 

  const places, restaurant, bar, coffee, bank, park = 
  this.props.results;

  if (prevProps.results.places !== places) 
      this.setState( places );
  
  if (prevProps.results.restaurant !== restaurant) 
      this.setState( places: restaurant );
  
  if (prevProps.results.bar !== bar) 
      this.setState( places: bar );
  
  if (prevProps.results.coffee !== coffee) 
      this.setState( places: coffee );
  
  if (prevProps.results.bank !== bank) 
      this.setState( places: bank );
  
  if (prevProps.results.park !== park) 
      this.setState( places: park );
  


renderPlaces = () => this.state.places.map((place, i) => 
  if (place.type || place.type === "Recommended Places") 
      return place.items.map((item, i) => (
          <tbody key=item.venue.id>
              <tr className="table-secondary">
                  <th scope="row">item.venue.name</th>
                  <td>
                      <h6>
                          `$(item.venue.location.distance / 
                           1609.344).toFixed(2)`" "
                          <cite>miles</cite>
                      </h6>
                  </td>
              </tr>
          </tbody>
      ));
  
  return this.state.places.map((place, i) => (
      <tbody key=place.id>
          <tr className="table-secondary">
              <th scope="row">place.name</th>
              <td>
                  <h6>
                      `$(place.location.distance / 
                      1609.344).toFixed(2)`" "
                      <cite>miles</cite>
                  </h6>
              </td>
          </tr>
      </tbody>
   ));
)

render() 
  return (
      <React.Fragment>
          this.renderPlaces()
      </React.Fragment>
  );
 


Results.propTypes = 
  places: PropTypes.array,
  restaurant: PropTypes.array,
  coffee: PropTypes.array,
  bar: PropTypes.array,
  banks: PropTypes.array,
  parks: PropTypes.array
;

下面是父组件-

import React,  Component  from "react";
import PropTypes from "prop-types";

import  connect  from "react-redux";
import Results from "./Results";

class ResultList extends Component 
  state = 
    places: [],
    restaurant: [],
    bar: [],
    coffee: [],
    bank: [],
    park: []
  ;

  getSnapshotBeforeUpdate(prevProps) 
    const 
      recommendedPlaces,
      restaurants,
      coffee,
      bars,
      banks,
      parks
     = this.props;

    if (prevProps.recommendedPlaces !== recommendedPlaces) 
      this.setState( places: recommendedPlaces );
      // returning a snapshot just in case I want to use..⬇
      // componentDidUpdate in the future
      return recommendedPlaces;
    
    if (prevProps.restaurants !== restaurants) 
      this.setState( restaurant: restaurants );
      // returning a snapshot just in case I want to use..⬇
      // componentDidUpdate in the future
      return restaurants;
    
    if (prevProps.bars !== bars) 
      this.setState( bar: bars );
      // returning a snapshot just in case I want to use..⬇
      // componentDidUpdate in the future
      return bars;
    
    if (prevProps.coffee !== coffee) 
      this.setState( coffee );
      // returning a snapshot just in case I want to use..⬇
      // componentDidUpdate in the future
      return coffee;
    
    if (prevProps.banks !== banks) 
      this.setState( bank: banks );
      // returning a snapshot just in case I want to use..⬇
      // componentDidUpdate in the future
      return banks;
    
    if (prevProps.parks !== parks) 
      this.setState( park: parks );
      // returning a snapshot just in case I want to use..⬇
      // componentDidUpdate in the future
      return parks;
    

    return null;
  

  render() 
    const  address, filterBy  = this.props;
    return (
      <table className="primary table table-hover">
        <thead>
          <tr>
            <th scope="col">
              filterBy === null ? "Places" : filterBy.toUpperCase()
              
                // eslint-disable-next-line
              " "
              near
              
                // eslint-disable-next-line
              " "
              address.toUpperCase()
            </th>
            /* this.showPlaces(recommendedPlaces, restaurants, bars, parks) */
            <th scope="col">Distance</th>
          </tr>
        </thead>
        <Results results=this.state />
      </table>
    );
  


ResultList.propTypes = 
  address: PropTypes.string,
  filterBy: PropTypes.string,
  recommendedPlaces: PropTypes.array,
  restaurants: PropTypes.array,
  coffee: PropTypes.array,
  bars: PropTypes.array,
  banks: PropTypes.array,
  parks: PropTypes.array
;

const mapStateToProps = state => (
  recommendedPlaces: state.places.recommendedPlaces,
  restaurants: state.places.restaurants,
  coffee: state.places.coffee,
  bars: state.places.bars,
  banks: state.places.banks,
  parks: state.places.parks
);

export default connect(mapStateToProps)(ResultList);

接下来的两张图片是渲染页面。

第一个是我搜索地点时的第一个渲染。完美渲染。它仅获取和呈现“推荐地点”。

第二个是当点击左侧的过滤器按钮时新道具进入时重置状态后发生的渲染。它不过滤地点数据。相反,它使用位置和过滤器获取并呈现新数据。

非常感谢任何帮助!

https://codesandbox.io/s/zw33318484 在自动完成搜索字段中搜索地址。它应该使用“推荐位置”呈现页面中间(ResultList.js 和 Result.js)。然后选择“餐厅”过滤器。现在它应该重新渲染商店中的餐厅。相反,它只是在渲染时不断添加餐厅。我认为它正在重新渲染并不断连接道具中的餐厅数组。它会重新渲染多次。

【问题讨论】:

请不要在问题中使用代码图片。改为包含实际代码。 请粘贴和编辑您的代码,而不是发送图片。你的问题的工作 sn-p 会更好 你为什么要把道具放入状态?如果你在 props 中收到你需要的东西,只需从 props 渲染。 @Dean 最有帮助的是一个显示行为的工作示例(例如 CodeSandbox)。尽可能简化(应该只需要两个过滤器,硬编码 redux 初始状态以及在选择过滤器后将哪些数据放入其中,以便不涉及获取),同时仍然重现问题。 @RyanCogswell 当然。在 Autocomple search 中搜索“386 2nd Avenue, New York, NY, USA”。然后选择“restaurant”过滤器。 【参考方案1】:

问题出在您的 renderPlaces 函数中。下面是一个简化版本,以便于查看结构。

renderPlaces = () => 
    this.state.places.map((place, i) => 
      if (place.type || place.type === "Recommended Places") 
          // When first selecting a location from the autocomplete search,
          // you execute this code
          return place.items.map((item, i) => (
              <tbody key=item.venue.id>
                  /* stuff to render a place */
              </tbody>
          ));
      
      // When you select a filter, you hit this condition instead.
      return this.state.places.map((place, i) => (
          <tbody key=place.id>
              /* stuff to render a place */
          </tbody>
       ));
    )
;

当您第一次选择位置时,this.state.places 中有一个条目,其中包含“推荐地点”的 place.typeplace 有一个驱动渲染的 items 数组。当您选择一个过滤器(例如“餐厅”)时,places 有多个条目(在我看到的情况下为 10 个)并且它们都没有type,因此它达到了第二个条件。第二次返回执行this.state.places.map 调用,但您仍在外部this.state.places.map 调用中,因此您对每个位置遍历所有位置一次。所以额外的副本与重新渲染的数量无关,而只是与地方的数量有关。如果您删除第二个this.state.places.map,如下所示,它可以正常工作(至少在这方面)。

renderPlaces = () => 
    this.state.places.map((place, i) => 
      if (place.type || place.type === "Recommended Places") 
          return place.items.map((item, i) => (
              <tbody key=item.venue.id>
                  /* stuff to render a place */
              </tbody>
          ));
      
      return (
          <tbody key=place.id>
              /* stuff to render a place */
          </tbody>
      );
    )
;

【讨论】:

天哪!那真是太愚蠢了。我浪费了半天时间把头发扯掉。非常感谢瑞恩!你就是男人!

以上是关于React 组件表在更新状态后被多次渲染或复制的主要内容,如果未能解决你的问题,请参考以下文章

React 组件有啥方法可以保持之前渲染的值吗? [复制]

如何使用 react-hooks (useEffect) 缓冲流数据以便能够一次更新另一个组件以避免多次重新渲染?

React hooks 功能组件防止在状态更新时重新渲染

如何重新渲染 React 组件?

3react-props/state

3react-props/state