Лучший способ фильтрации таблицы в реакторе

У меня есть массив объектов, хранящихся в редуксе. Я хочу, чтобы иметь возможность фильтровать этот массив на основе ввода пользователем. Должен ли я создать объект состояния, который получает массив через реквизит и модифицирует этот массив, или это плохая практика для смешивания состояния и реквизита? Если это нормально, смешайте два, должен ли я установить состояние в компонентеWillReceiveProps?

Ответ 1

Построение состояния на основе реквизита может быть несколько сложным, что приемлемо, но вы должны рассмотреть все свои варианты.

Самым простым для реализации является фильтрация реквизита в вашем методе render. Если у вас достаточно маленькие компоненты, которые не обновляются по слишком многим причинам, особенно если число элементов в списке невелико, это может быть предпочтительный метод:

class FilterList extends React.Component {
  render () {
    const { elements } = this.props;
    const { filterStr } = this.state;

    const filteredElements = elements
      .filter(e => e.includes(filterStr))
      .map(e => <li>{ e }</li>)

    return (
      <div>
        <input
          type="text"
          value={ filterStr }
          onChange={ e => this.setState({ filterStr: e.target.value }) } />
        <ul>
          { filteredElements }
        </ul>
      </div>
    );
  }
}

Следующий вариант - сделать то, что вы описываете, и получить вычисленное состояние на основе состояния фильтра компонента и пропущенных ему реквизитов. Это хорошо, когда у вас есть сложный компонент, который получает много реквизитов и часто отображается. Здесь вы кэшируете видимые элементы и фильтруете список только тогда, когда его нужно отфильтровать.

class FilterList extends React.Component {
  constructor (props) {
    this.state = {
      viewableEls: props.elements
    }
  }

  componentWillReceiveProps (nextProps) {
    const { elements } = this.props;
    const { filterStr } = this.state;

    if (elements !== nextProps.elements) {
      this.setState({
        viewableEls: this.getViewableEls(nextProps.elements, filterStr)
      })
    }
  }

  getViewableEls (elements, filterStr) {
    return elements.filter(el => el.includes(filterStr))
  }

  handleFilterChange = e => {
    const { elements } = this.props;

    this.setState({
      filterStr: e.target.value,
      viewableEls: this.getViewableEls(elements, filterStr)
    })
  }
  render () {
    const { viewableEls } = this.state;

    return (
      <div>
        <input
          type="text"
          value={ filterStr }
          onChange={ this.handleFilterChange } />
        <ul>
          { viewableEls.map(e => <li key={ e }>{ e }</li>) }
        </ul>
      </div>
    );
  }
}

И, наконец, "путь" для filterStr, который требует, чтобы вы передавали создатель действия и filterStr качестве реквизита для компонента, вероятно, передавался через connect где-то еще. Реализация ниже использует компонент без сохранения состояния, так как мы не fitlerStr в состоянии компонента вообще.

const FilterTable = ({ elements, filterStr, changeFilterStr }) => {
  return (
    <div>
      <input
        type="text"
        value={ filterStr }
        onChange={ e => changeFilterStr(e.target.value) } />
      <ul>
        {
          elements
            .filter(e => e.includes(filterStr))
            .map(e => <li key={ e }>{ e }</li>)
        }
      </ul>
    </div>
  )
}