web-dev-qa-db-ja.com

React-setStateを使用せずに状態を変更する:回避する必要がありますか?

コードは機能しますが、ベストプラクティスの質問があります。状態にオブジェクトの配列があり、ユーザーの操作によって一度に1つのオブジェクトの値が変更されます。私の知る限り、状態を直接変更することは想定されていません。代わりに常にsetStateを使用する必要があります。それを何らかの価格で回避したい場合は、反復によって配列をディープクローンし、クローンを変更します。次に、状態をクローンに設定します。私の意見では、とにかく後で変更する状態を変更しないようにすると、パフォーマンスが低下します。

詳細バージョン: this.state.dataはオブジェクトの配列です。これはフォーラムのトピックのリストを表し、お気に入りボタンはclickCollect()を呼び出して切り替わります。状態に配列があるので、1つのアイテムのis_collectedプロパティを変更する場合、操作する配列のコピーを作成する必要があり、新しい値に変更した後、それを状態に設定できます。

_var data = this.state.data.slice(0);
data[index].is_collected = !data[index].is_collected;
this.setState({data: data});
_

_var data = this.state.data_:これはポインタを配列にコピーし、Push()、shift()などが状態を直接変更します。 dataと_this.state.data_の両方が影響を受けます。

var data = this.state.data.slice(0):これは浅いクローンを作成します。プッシュアンドシフトは状態を変更しませんが、私のクローンにはまだ状態の配列の要素へのポインターがあります。したがって、_data[0].is_collected_を変更すると、_this.state.data[0].is_collected_も変更されます。これは、setState()を呼び出す前に発生します。

通常私はすべきです:

_var data = []; 
for (var i in this.state.data) {
    data.Push(this.state.data[i]); 
}
_

次に、インデックスの値を変更し、falseの場合はtrueに、trueの場合はfalseに設定します。

_data[index].is_collected = !data[index].is_collected;
_

そして状態を変更します:

_this.setState({data: data});
_

私の配列が比較的大きいか、非常に大きいと考えると、この反復によりAPPのパフォーマンスが低下すると思います。それが何らかの理由で正しい方法であることを知っていれば、私はその費用を支払います。ただし、この関数(clickCollect)では常に新しい値を状態に設定しているため、変更を中止するという誤ったAPI応答を待機していません。すべての場合において、新しい値は状態に入ります。実際には、UIを再度レンダリングする場合にのみsetStateを呼び出します。だから質問は:

  1. この場合、ディープクローンを作成する必要がありますか? (_for var i in ..._)
  2. そうでない場合、配列にオブジェクトが含まれている場合、浅いクローン(.slice(0))を作成することは理にかなっていますか?変更は配列内のオブジェクトに対して行われているので、コピー(_data = this.state.data_)が行うのと同じように、浅いクローンはまだ私の状態を変更します。

私のコードは単純化され、API呼び出しは簡略化のために省略されています。

これは初心者の質問なので、まったく異なるアプローチも歓迎します。または、他のQ&Aへのリンク。

_import React from 'react';

var ForumList = React.createClass({
  render: function() {
      return <div className="section-inner">
        {this.state.data.map(this.eachBox)}
      </div>
  },
  eachBox: function(box, i) {
    return <div key={i} className="box-door">
        <div className={"favorite " + (box.is_collected ? "on" : "off")} onTouchStart={this.clickCollect.bind(null, i)}>
          {box.id}
        </div>
    </div>
  },
  getInitialState: function() {
    return {data: [
      {
        id: 47,
        is_collected: false
      },
      {
        id: 23,
        is_collected: false
      },
      {
        id: 5,
        is_collected: true
      }
    ]};
  },
  clickCollect: function(index) {
    var data = this.state.data.slice(0);
    data[index].is_collected = !data[index].is_collected;
    this.setState({data: data});
  }
});

module.exports = ForumList;
_
9
Szalai Laci

個人的に私はいつもルールに従うわけではありません。あなたが何をしようとしているのかを本当に理解していれば、それは問題ではないと思います。

var data = this.state.data.slice(0);
data[index].is_collected = !data[index].is_collected;
this.setState({data: data});

この場合、状態を変更し、このようにsetStateを再度呼び出すと問題ありません。

this.state.data[index].is_collected = !this.state.data[index].is_collected;
this.setState({data: this.state.data});

状態の変更を避けるべき理由は、this.state.dataへの参照があり、setStateを複数回呼び出すと、データが失われる可能性があるためです。

const myData = this.state.data
myData[0] = 'foo'
this.setState({ data: myData })
// do something...
// ...
const someNewData = someFunc()
this.setState({ data: someNewData })

myData[1] = 'bar' // myData is still referencing to the old state
this.setState({ data: myData }) // you lose everything of `someNewData`

これについて本当に心配している場合は、 immutable.js

3
CodinCat

反応のベストプラクティスに従う場合は、プロパティを変更するときに、すべての配列の浅いコピーを実行する必要があります。 「不変」ライブラリの実装を調べてください。

しかし、私の経験と私の意見から、 "shouldCompomenentUpdate"実装がある場合はsetStateメソッドを呼び出す必要があります。あなたの浅いコピーがはるかに多くのリソースを消費すると思い、仮想domチェックに反応する場合、これを行うことができます:

this.state.data[0].property = !this.state.data[0].property;
this.forceUpdate();
1
degr

状態をミュートすると、Reactのデータフロー(一方向にされる)の主要な原則が直接破られ、アプリが非常に壊れやすくなり、基本的にコンポーネントのライフサイクル全体が無視されます。

そのため、setState({})を使用せずにコンポーネントの状態を変更することを妨げるものは何もありませんが、Reactを実際に利用したい場合は、それを絶対に回避する必要があります。

0
Kelvin De Moya

私があなたの質問を正しく理解していれば、オブジェクトの配列があり、配列内の単一のオブジェクトのプロパティが変化すると、

  1. 配列のディープクローンを作成し、setStateに渡します
  2. 浅いクローンを作成し、setStateに渡します

reduxsample todo app で確認したところ、オブジェクトの単一のプロパティが変更された場合は、配列全体ではなく、その単一のオブジェクトの新しいコピーを作成する必要があります。 reduxについて読み、可能であればアプリの状態を管理することをお勧めします。

0
VJAI