为什么componentDidUpdate()无法检测到我组件的状态变化?

时间:2018-10-02 11:06:08

标签: reactjs

我有一张船的桌子,并且正在尝试实现排序(使用表标题单击)和过滤(使用用户键入的文本字段)。

我对React如何处理组件状态感到困惑。

我的理解是componentDidUpdate()的工作方式如下:

  • 我在某处更改了组件状态
  • 状态更改由组件检测,并且componentDidUpdate()运行

基于这种理解,我希望componentDidUpdate()要

  • 当我改变船的状态时重新排序
  • 当我改变船只状态时重新过滤

但是,当触发排序时,不会进行过滤。

我认为这会发生:

  • 状态已更改,触发componentDidUpdate()
  • 发货已排序
  • 状态已保存
  • 状态保存会触发componentDidUpdate()重新运行
  • this.state.ships现在不同于prevState.ships,触发重新过滤

但这似乎发生了:

  • 状态已更改,触发componentDidUpdate()
  • 发货已排序
  • 状态已保存
  • 状态保存会触发componentDidUpdate()重新运行
  • this.state.ships与prevState.ships相同,不会触发重新过滤

所以我对componentDidUpdate()的理解是参差不齐的,或者我对状态同步性的理解是。我已经读过状态可以在事件处理程序中异步。当我尝试检测是否应过滤时,也许分类后的船只还没有保存到状态中?

import React, { Component } from 'react';

import { SearchBar } from '../SearchBar';
import { Table } from '../Table/Table';
import { MoreButton } from '../MoreButton/MoreButton';

export class SearchableSortableTable extends Component {
  constructor(props) {
    super(props);
    this.fetchShips = this.fetchShips.bind(this);
    this.filterShips = this.filterShips.bind(this);
    this.setSearchExpression = this.setSearchExpression.bind(this);
    this.setSort = this.setSort.bind(this);
    this.state = {
      ships: [],
      filteredShips: [],
      searchExpression: '',
      reverseSort: false
    };
  }

  render() {
    return (
      this.state.error ?
        <div>
          <div>There was a problem fetching the ships, sorry.</div>
          <div>{this.state.error}</div>
        </div>
        :
        this.state.ships.length === 0 ? <h4>Loading...</h4> :
          <div>
            <div>
              <SearchBar setSearchExpression={this.setSearchExpression} />
              <MoreButton className="di" url={this.state.nextUrl} fetchShips={this.fetchShips} />
            </div>
            <div>
              <Table ships={this.state.filteredShips} setSort={this.setSort} sortBy={this.state.columnName} reverse={this.state.reverseSort} />
            </div>
          </div>
    );
  }

  componentDidMount() {
    this.fetchShips(this.props.url);
  }

  componentDidUpdate(prevProps, prevState) {
    if (this.state.columnName !== prevState.columnName || this.state.reverseSort !== prevState.reverseSort) {
      this.sortShips();
    }

    // This conditional block is not entered when I sort.
    if (this.state.ships !== prevState.ships || this.state.searchExpression !== prevState.searchExpression) {
      this.filterShips();
    }
  }

  async fetchShips(url) {
    try {
      const response = await fetch(url);
      if (response['status'] && response['status'] === 200) {
        const json = await response.json();
        const ships = json['results'].map(this.mapShip);

        this.setState({
          ships: this.state.ships.concat(ships),
          nextUrl: json['next']
        });
      } else {
        this.setState({ error: `${response['status']} ${response['statusText']}` });
      }
    } catch (error) {
      if (error instanceof TypeError && error.message.includes('NetworkError')) {
        this.setState({ error: `${error.name} ${error.message}` });
      } else {
        throw error;
      }
    }
  }

  filterShips() {
    const filteredShips = this.state.ships.filter(ship => {
      return Object.values(ship).some(shipProp => shipProp.includes(this.state['searchExpression']))
    });
    this.setState({
      filteredShips: filteredShips
    });
  }

  setSearchExpression(event) {
    this.setState({ searchExpression: event.target.value });
  }

  setSort(event) {
    if (event && event['currentTarget'] && event['currentTarget']['attributes'] &&
      event['currentTarget']['attributes']['name'] && event['currentTarget']['attributes']['name']['nodeValue']) {
      const columnName = event['currentTarget']['attributes']['name']['nodeValue'];
      this.setState({
        columnName,
        reverseSort: columnName === this.state.columnName ? !this.state.reverseSort : false
      });
    }
  }

  sortShips() {
    if (this.state.columnName) {
      const sortedShips = this.state.ships.sort((a, b) => {
        const propA = a[this.state.columnName];
        const propB = b[this.state.columnName];
        if (!isNaN(+propA)) {
          return this.state.reverseSort ? Number(propB) - Number(propA) : Number(propA) - Number(propB);
        }
        return this.state.reverseSort ? propB.localeCompare(propA) : propA.localeCompare(propB);
      });
      this.setState({ ships: sortedShips });
    }
  }

  /**
   * Maps a ship to its name, manufacturer, cost and starship class.
   * @param ship The ship to be mapped.  
   */
  mapShip(ship) {
    const { name, manufacturer, cost_in_credits, starship_class } = ship;
    return Object.assign(
      {
        name,
        manufacturer,
        cost_in_credits,
        starship_class
      },
      {}
    );
  }
}

1 个答案:

答案 0 :(得分:0)

shouldComponentUpdate()方法适用于道具和状态。在您的示例中,在sort / filter事件之后,React将触发以下方法。尝试使用,

 shouldComponentUpdate(nextProps, nextState) {
  return this.state.value != nextState.value;
  }