コンポーネント状態の配列から要素を削除しています


131

コンポーネントの状態で配列から要素を削除する最良の方法を見つけようとしています。this.state変数を直接変更するべきではないので、配列から要素を削除するより良い方法(より簡潔)はここにあるものよりも優れていますか?

  onRemovePerson: function(index) {
    this.setState(prevState => { // pass callback in setState to avoid race condition
      let newData = prevState.data.slice() //copy array from prevState
      newData.splice(index, 1) // remove element
      return {data: newData} // update state
    })
  },

ありがとうございました。

更新しました

これは、setStateでコールバックを使用するように更新されました。これは、更新中に現在の状態を参照するときに行う必要があります。


Reactでうまく機能するFacebookのImmutableJSを見てください。リンク
Jonatan LundqvistMedén2015

6
コードに問題はありません。実際、それは非常に慣用的な方法です。
Dimitar Dimitrov

回答:


138

私が見たこれを行う最もきれいな方法はfilter次のとおりです:

removeItem(index) {
  this.setState({
    data: this.state.data.filter((_, i) => i !== index)
  });
}

18
@HussienKは_、未使用の引数を表すために使用されることがあります。ここでは、配列の現在のアイテムです。
chrisM 2016年

1
すごい。私は常に.filterを使用していますが、この状況で使用することは考えていません。:D
dakt

4
これはクリーンなコードですが、大きな配列の場合、この方法は遅くなります。理由は、配列全体をループして、すでに決定されているように見えるインデックスを見つけるためです。
マット・エリス

1
@MattEllis Reactの状態の更新はとにかく不変であるため、リストのサイズにO(n)が発生し、どのような場合でもそれをコピーすることになります。これがパフォーマンスに大きな影響を与えたとしたら、私は驚きます。
エフライオン2017年

4
this.stateへの入力として評価することはthis.setState()、不変であっても、お勧めできません。このトピックに関する公式のReactドキュメントへの参照については、上記の私の回答を参照してください。
pscl 2018

87

update()不変性ヘルパーをreact-addons-update使用することもできます。これは事実上同じことを内部で行いますが、あなたがやっていることは問題ありません。

this.setState(prevState => ({
  data: update(prevState.data, {$splice: [[index, 1]]})
}))

あなたがリンクしたものよりもはるかに単純な例:)
公園。

7
react-addons-updateは非推奨になりました(2016)。immutability-helper代替品としてご利用いただけます。github.com/kolodny/immutability-helper this.setState()内でthis.stateを直接変更しないことについては、以下の私の回答も参照してください。
pscl 2017

62

this.state内部での参照setState()はお勧めできません(状態の更新が非同期である可能性があります))。

ドキュメントは使用をお勧めします setState()は、更新が発生したときに実行時にprevStateが渡されるように、コールバック関数するています。したがって、これは次のようになります。

ES6なしでArray.prototype.filterを使用する

removeItem : function(index) {
  this.setState(function(prevState){
    return { data : prevState.data.filter(function(val, i) {
      return i !== index;
    })};
  });
}

ES6の矢印関数でArray.prototype.filterを使用する

removeItem(index) {
  this.setState((prevState) => ({
    data: prevState.data.filter((_, i) => i !== index)
  }));
}

不変性ヘルパーの使用

import update from 'immutability-helper'
...
removeItem(index) {
  this.setState((prevState) => ({
    data: update(prevState.data, {$splice: [[index, 1]]})
  }))
}

スプレッドの使用

function removeItem(index) {
  this.setState((prevState) => ({
    data: [...prevState.data.slice(0,index), ...prevState.data.slice(index+1)]
  }))
}

使用される手法に関係なく、各インスタンスでthis.setState()は、oldへのオブジェクト参照ではなく、コールバックが渡されることに注意してくださいthis.state



1
さまざまな手法を追加したprevStateコールバックの使用例。
pscl 2017

このようにするとどうなりますか? @ user1628461が示すコールバックオプションの代わりthis.setState({ data: [...this.state.data.slice(0, index), ...this.state.data.slice(index + 1)] }); に使用this.stateするのは間違っていますprevStateか?
Tiberiu Maxim

これは最良の解決策ですが、最も単純ではありません
Developia

おかげで、spreadを使用して、要素を削除するのではなく、途中で更新することもできます。
Vaibhav Vishal

24

ES6スプレッド構文を使用して、状態の配列から要素を削除する方法を次に示します。

onRemovePerson: (index) => {
  const data = this.state.data;
  this.setState({ 
    data: [...data.slice(0,index), ...data.slice(index+1)]
  });
}

1
ありがとう!これは、最も自然な方法のように感じます。
fabiomaia 2017

3

他の誰かが私の同じ問題に遭遇した場合に備えて、この質問は@psclによってすでに正しく回答されています、ここでチャイムを鳴らしたいと思います。4つの方法のうち、簡潔で外部ライブラリへの依存がないため、矢印関数でes6構文を使用することを選択しました。

ES6の矢印関数でArray.prototype.filterを使用する

removeItem(index) {
  this.setState((prevState) => ({
    data: prevState.data.filter((_, i) => i != index)
  }));
}

ご覧のとおり、私の場合は文字列フィールドからインデックスを取得していたため、インデックスのタイプ(!==to !=)を無視するように少し変更しました。

クライアント側で要素を削除するときに奇妙な動作が見られる場合のもう1つの有用な点は、配列のインデックスを要素のキーとして使用しないことです。

// bad
{content.map((content, index) =>
  <p key={index}>{content.Content}</p>
)}

Reactが変更時に仮想DOMと比較するとき、Reactはキーを調べて何が変更されたかを判断します。したがって、インデックスを使用していて、配列内のインデックスが1つ少ない場合、最後のインデックスが削除されます。代わりに、次のように、コンテンツのIDをキーとして使用します。

// good
{content.map(content =>
  <p key={content.id}>{content.Content}</p>
)}

上記は関連する投稿からのこの回答からの抜粋です。

みんなハッピーコーディング!


1

要素(インデックスなし)を削除する場合は、この関数を使用できます。

removeItem(item) {
  this.setState(prevState => {
    data: prevState.data.filter(i => i !== item)
  });
}

1

上記のエフリオンの回答へのコメントで述べたように、filter()は、特に大きな配列の場合、ループがすでに決定されているように見えるインデックスを探すためにループするため、遅くなる可能性があります。これはクリーンですが非効率的なソリューションです。

別の方法として、目的の要素を単に「スライス」して、フラグメントを連結することができます。

var dummyArray = [];    
this.setState({data: dummyArray.concat(this.state.data.slice(0, index), this.state.data.slice(index))})

お役に立てれば!


0

1行のヘルパー関数を使用して、コードを読みやすくすることができます。

const removeElement = (arr, i) => [...arr.slice(0, i), ...arr.slice(i+1)];

次に、次のように使用します。

this.setState(state => ({ places: removeElement(state.places, index) }));

0

ただの提案let newData = prevState.dataですが、使用するコードの代わりに、ES6で導入されたスプレッドを使用できます。let newData = ...prevState.data 配列のコピーに

3つのドット...表しスプレッド演算子休息のパラメータを

これにより、配列式、文字列、または反復可能なすべてのものを、関数呼び出しの0個以上の引数または配列の要素が期待される場所で展開できます。

さらに、次のようにして配列から項目を削除できます

onRemovePerson: function(index) {
  this.setState((prevState) => ({
    data: [...prevState.data.slice(0,index), ...prevState.data.slice(index+1)]
  }))
}

これが貢献することを願っています!!


-3

これを行う簡単な方法を次に示します。

removeFunction(key){
  const data = {...this.state.data}; //Duplicate state.
  delete data[key];                  //remove Item form stateCopy.
  this.setState({data});             //Set state as the modify one.
}

それが役に立てば幸い!!!


説明してもいいですか?
Tiberiu Maxim 2017

5
私はそれはdeleteアイテムを削除するがインデックスは更新されないためだと思うので、これは通常のニーズには適したアプローチではないでしょう。
クノック2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.