web-dev-qa-db-ja.com

同じ反応コンポーネントでreduxストア状態とともにローカル状態を使用するにはどうすればよいですか?

連絡先を表示するテーブルがあり、連絡先を名で並べ替えたい。連絡先配列はreduxストアから取得され、小道具を介して送信されますが、ローカルUIはローカルUI状態であるため、これらの連絡先の並べ替え方法をローカル状態に保持する必要があります。どうすればこれを達成できますか?これまで連絡先をcomponentWillReceivePropsに配置しましたが、何らかの理由で、変更時に小道具を受け取りません。 reduxストアの状態が変わるたびにローカル状態を更新するにはどうすればよいですか?

const Table = React.createClass({
  getInitialState () {
    return {contacts: []}
  },
  componentWillReceiveProps () {
    this.setState({ contacts: this.props.data.contacts})
  },
  sortContacts (parameter, e){
    ...
  },
  render () {
    return (
      <table>
        <thead>
          <tr>
            <th onClick={this.sortContacts.bind(this, "firstName")}>First Name</th>
          </tr>
        </thead>
        <tbody>
          {contactRows}
        </tbody>
      </table>
    )
  }
})

フィルタリングを含む現在のコードの更新

import React, {Component} from 'react'
import TableRow from './TableRow'

class Table extends Component {
  constructor (props) {
    super(props)
    this.state = { sortBy: "fistName" }
  }
  sortContacts (parameter) {
    console.log('in sortContacts')

    this.setState({ sortBy: parameter })
  }
  sortedContacts () {
    console.log('in sortedContacts')

    const param = this.state.sortBy
    return (
      this.props.data.contacts.sort(function (a, b){
        if (!a.hasOwnProperty(param)){
          a[param] = " ";
        }
        if (!b.hasOwnProperty(param)){
          b[param] = " ";
        }
        const nameA = a[param].toLowerCase(), nameB = b[param].toLowerCase();
        if (nameA > nameB) {
          return 1;
        } else {
          return -1;
        }
      })
    )
  }
  filteredSortedContacts () {
    console.log('in filteredSortedContacts')

    const filterText = this.props.data.filterText.toLowerCase()
    let filteredContacts = this.sortedContacts()
    if (filterText.length > 0) {
      filteredContacts = filteredContacts.filter(function (contact){
        return (
          contact.hasOwnProperty('lastName') &&
          contact.lastName.toLowerCase().includes(filterText)
        )
      })
    }
    return filteredContacts
  }
  contactRows () {
    console.log('in contactRows')
    return this.filteredSortedContacts().map((contact, idx) =>
      <TableRow contact={contact} key={idx}/>
    )
  }
  render () {
    return (
      <div className="table-container">
        <table className="table table-bordered">
          <thead>
            <tr>
              <th className="th-cell" onClick={this.sortContacts.bind(this, "firstName")}>First Name</th>
              <th onClick={this.sortContacts.bind(this, "lastName")}>Last Name</th>
              <th>Date of Birth</th>
              <th>Phone</th>
              <th>Email</th>
              <th>Notes</th>
            </tr>
          </thead>
          <tbody>
            {this.contactRows()}
          </tbody>
        </table>
      </div>
    )
  }
}

export default Table

私が今見ている問題は、contactRows, filteredSortedContacts, sortedContactsは、TableRowごとに1回、複数回呼び出されています。本文でcontactRowsを1回だけ呼び出すと、これがどのように発生するかわかりません。

14
stackjlei

Reduxストアとローカルストアの両方を使用するアプローチは正しいです。

コンポーネント内のreduxストアから状態を複製しようとしないでください。小道具でそれを参照してください。

代わりに、ローカルに保存されたsortedContacts paramをreduxに保存された連絡先に適用することにより、その場で値を計算するsortBy関数を作成します。

const Table extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      sortBy: 'id' // default sort param
    }
  }

  sortContacts(param) {
    this.setState({ sortBy: param})
  }

  sortedContacts() {
    return this.props.contacts.sort(...); // return sorted collection
  }

  render() {
    return (
      <table>
        <thead>
          <tr>
            <th onClick={() => this.sortContacts("firstName")}>First Name</th>
          </tr>
        </thead>
        <tbody>
          {this.sortedContacts()}
        </tbody>
      </table>
    )
  }
}
9
Michał Szajbe

componentWillReceiveProps()メソッドは、最初のレンダリングでは呼び出されません。小道具からのデータのみを初期データとして使用する場合は、次のようにします。

_getInitialState () {
  return {
    contacts: this.props.data.contacts
  }
}
_

React docs では、小道具の唯一の目的が何かを内部で初期化することであることを明確にするために、小道具にinitialContactsという名前を付けることを提案しています。

_this.props.contacts_が変更されたときに更新したい場合は、componentWillReceiveProps()を使用できます。しかし、それが最良のアイデアであるかどうかはわかりません。ドキュメントから:

小道具を使用してgetInitialStateで状態を生成すると、多くの場合、「真実の源」、つまり実際のデータが重複することになります。これは、コンポーネントが最初に作成されたときにのみgetInitialStateが呼び出されるためです。

可能な場合は常に値をオンザフライで計算し、後で同期がずれてメンテナンスの問題が発生しないようにします。

3
tobiasandersen

React 16.3には、静的関数getDerivedStateFromProps(nextProps, prevState)が含まれています。これは、初期マウント後および新しい小道具を受け取ったときに呼び出されます。 https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops を参照してください

2
Jesper Lehtinen