状态更新后,React组件表将呈现多次或重复

时间:2019-01-19 19:22:04

标签: javascript arrays reactjs redux react-redux

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

这是应用程序的工作流程-用户搜索位置时,首先发生获取操作。它会在用户位置周围显示“推荐位置”。它将数据作为“推荐位置”放入redux存储。有问题的组件从商店渲染数据[第一张图片],这很好。单击过滤器按钮时,这次它将获取用户位置和过滤器,并将数据放入redux商店中,例如restaurant = [],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);

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

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

enter image description here

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

enter image description here

非常感谢您的帮助!

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

1 个答案:

答案 0 :(得分:2)

问题出在您的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.type。该place具有一个驱动渲染的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>
      );
    })
};

Edit nearby