ReactJSでの状態配列の正しい修正


416

state配列の最後に要素を追加したいのですが、これは正しい方法ですか?

this.state.arrayvar.push(newelement);
this.setState({arrayvar:this.state.arrayvar});

配列をインプレースで変更するpushと問題が発生するのではないかと心配しています-安全ですか?

アレイのコピーを作成し、setStateそれを無駄にすることの代替案。


9
反応不変性ヘルパーを使用できると思います。これを参照してください:facebook.github.io/react/docs/update.html#simple-push
nilgun 2014年

状態配列のsetStateでソリューションを確認してください。<br/> <br> stackoverflow.com/a/59711447/9762736
Rajnikant Lodhi

回答:


763

ドキュメントが反応言います:

this.stateを不変であるかのように扱います。

あなたは、pushあなたが、その後、再び状態を「リセット」されている場合でも、直接、状態を変異し、それが潜在的に発生しやすいコードをエラーにつながる可能性があります。F.ex、それはcomponentDidUpdateトリガーしないなどのいくつかのライフサイクルメソッドにつながる可能性があります。

後のバージョンのReactで推奨されるアプローチは、競合状態を防ぐために状態を変更するときにアップデーター関数を使用することです。

this.setState(prevState => ({
  arrayvar: [...prevState.arrayvar, newelement]
}))

メモリの「無駄」は、非標準の状態変更を使用したときに直面する可能性のあるエラーと比較すると問題ではありません。

以前のReactバージョンの代替構文

concatそれは新しい配列を返すので、あなたはきれいな構文を得るために使うことができます:

this.setState({ 
  arrayvar: this.state.arrayvar.concat([newelement])
})

ES6では、Spread Operatorを使用できます。

this.setState({
  arrayvar: [...this.state.arrayvar, newelement]
})

8
競合状態が発生するタイミングの例を教えてください。
soundly_typed 2015

3
@Qiming pushは新しい配列の長さを返すため、機能しません。また、setState非同期であり、Reactは複数の状態変更を単一のレンダーパスにキューイングできます。
David Hellsing、2015

2
@mindeavorは、this.stateでパラメーターを検索するanimationFrameと、状態が変化すると他のパラメーターを変更する別のメソッドがあると言います。setStateは非同期であるため、状態が変更されても、変更をリッスンするメソッドに反映されないフレームがいくつかある可能性があります。
David Hellsing

1
@ChristopherCampsこの回答はsetState、2回の呼び出しを奨励するものではなく、直接変更することなく状態配列を設定する2つの類似した例を示しています。
David Hellsing 2017

2
最近、状態配列を不変として扱う簡単な方法は次のとおりlet list = Array.from(this.state.list); list.push('woo'); this.setState({list});です。もちろん、スタイルの好みに合わせて変更してください。
basicdays 2017

133

を使用してES6いる場合、最も簡単です。

initialArray = [1, 2, 3];

newArray = [ ...initialArray, 4 ]; // --> [1,2,3,4]

新しいアレイは [1,2,3,4]

Reactで状態を更新するには

this.setState({
         arrayvar:[...this.state.arrayvar, newelement]
       });

アレイの解体の詳細


@ムジエットは詳しく説明できますか?
StateLess、2017年

4
この問題の核心は、配列を変更するのではなく、Reactの状態を変更することです。あなたは私のポイントを自分で見て、あなたの答えを編集しました。これはあなたの答えを適切にします。よくやった。
Marco Faustinelli、2017年

2
あなたの質問はOPの質問に直接関係していません
StateLess

1
@ChanceSmith:StateLessの回答でも必要です。状態の更新を状態自体に依存しないでください。公式ドキュメント:reactjs.org/docs/...
ARCOL

1
@RayCoderがログに記録し、arrayvarの値を確認します。配列ではないようです。
StateLess

57

を使用した最も簡単な方法ES6

this.setState(prevState => ({
    array: [...prevState.array, newElement]
}))

申し訳ありませんが、私の場合、配列を配列にプッシュします。tableData = [['test','test']]新しいアレイをプッシュした後tableData = [['test','test'],['new','new']]。この@Davidと@Riddをプッシュする方法
Johncy '21

@Johncy [['test','test'],['new','new']]試してみたい場合:this.setState({ tableData: [...this.state.tableData, ['new', 'new']]
Ridd

this.setState({ tableData: [...this.state.tableData ,[item.student_name,item.homework_status_name,item.comments===null?'-':item.comments] ] });新しい配列を2回挿入し this.state.tableData.push([item.student_name,item.homework_status_name,item.comments===null?'-':item.comments]);ます。希望どおりの結果が得られます。しかし、それは私が考える正しい方法ではありません。
ジョンシー2018

26

Reactは更新をバッチ処理する場合があるため、正しい方法は、setStateに更新を実行する関数を提供することです。

React更新アドオンの場合、以下が確実に機能します。

this.setState( state => update(state, {array: {$push: [4]}}) );

またはconcat()の場合:

this.setState( state => ({
    array: state.array.concat([4])
}));

次の例は、https://jsbin.com/mofekakuqi/7/editjsの出力を示しています

ReactはsetTimeoutコールバック内で更新をバッチ処理しないため、setTimeout()呼び出しは3つの項目を正しく追加します(https://groups.google.com/d/msg/reactjs/G6pljvpTGX0/0ihYw2zK9dEJを参照)。

バグのあるonClickは「3番目」のみを追加しますが、修正されたものは、期待どおりにF、S、Tを追加します。

class List extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      array: []
    }

    setTimeout(this.addSome, 500);
  }

  addSome = () => {
      this.setState(
        update(this.state, {array: {$push: ["First"]}}));
      this.setState(
        update(this.state, {array: {$push: ["Second"]}}));
      this.setState(
        update(this.state, {array: {$push: ["Third"]}}));
    };

  addSomeFixed = () => {
      this.setState( state => 
        update(state, {array: {$push: ["F"]}}));
      this.setState( state => 
        update(state, {array: {$push: ["S"]}}));
      this.setState( state => 
        update(state, {array: {$push: ["T"]}}));
    };



  render() {

    const list = this.state.array.map((item, i) => {
      return <li key={i}>{item}</li>
    });
       console.log(this.state);

    return (
      <div className='list'>
        <button onClick={this.addSome}>add three</button>
        <button onClick={this.addSomeFixed}>add three (fixed)</button>
        <ul>
        {list}
        </ul>
      </div>
    );
  }
};


ReactDOM.render(<List />, document.getElementById('app'));

実際にそれが起こるケースはありますか?単純に行う場合this.setState( update(this.state, {array: {$push: ["First", "Second", "Third"]}}) )
アルビシア

1
@Albizia同僚を見つけて、彼らと話し合うべきだと思います。setStateを1回だけ呼び出す場合は、バッチ処理の問題はありません。重要なのは、Reactが更新をバッチ処理することを示すことなので、そうです...実際にケースがあります。これは、上記のコードのJSBinバージョンで見つけることができます。このスレッドの回答のほとんどすべてがこれに対処できていないため、多くのコードがそこにあり、時々失敗します
NealeU

state.array = state.array.concat([4])これにより、前の状態オブジェクトが変化します。
エミールベルジェロン

1
@EmileBergeron粘り強くありがとうございます。私は最終的に振り返って、私の脳が見ることを拒否していたものを見て、ドキュメントをチェックしたので、編集します。
NealeU

1
良い!JSの不変性は自明ではないので、間違いを犯すのは本当に簡単です(ライブラリのAPIを扱う場合はなおさらです)。
エミールベルジェロン

17

@nilgunがコメントで述べたように、反応不変性ヘルパーを使用できます。これはとても便利だと思いました。

ドキュメントから:

シンプルなプッシュ

var initialArray = [1, 2, 3];
var newArray = update(initialArray, {$push: [4]}); // => [1, 2, 3, 4]

initialArrayはまだ[1、2、3]です。


6
Reactの不変性ヘルパーは、ドキュメントで非推奨として説明されています。 代わりにgithub.com/kolodny/immutability-helperを使用する必要があります。
Periata Breatta 2017年

3

機能部品を使用している場合は、以下のように使用してください。

const [chatHistory, setChatHistory] = useState([]); // define the state

const chatHistoryList = [...chatHistory, {'from':'me', 'message':e.target.value}]; // new array need to update
setChatHistory(chatHistoryList); // update the state

1

配列に新しい要素を追加push()するには、答えが必要です。

要素を削除して配列の状態を更新するには、以下のコードが私のために機能します。splice(index, 1)動作しません。

const [arrayState, setArrayState] = React.useState<any[]>([]);
...

// index is the index for the element you want to remove
const newArrayState = arrayState.filter((value, theIndex) => {return index !== theIndex});
setArrayState(newArrayState);

0

配列の状態で値をプッシュし、このように値を設定し、マップ関数で状態配列を定義して値をプッシュしようとしています。

 this.state = {
        createJob: [],
        totalAmount:Number=0
    }


 your_API_JSON_Array.map((_) => {
                this.setState({totalAmount:this.state.totalAmount += _.your_API_JSON.price})
                this.state.createJob.push({ id: _._id, price: _.your_API_JSON.price })
                return this.setState({createJob: this.state.createJob})
            })

0

これは私が配列内に配列を追加するのに役立ちました

this.setState(prevState => ({
    component: prevState.component.concat(new Array(['new', 'new']))
}));

0

2020年のReactjsフックの例を次に示します。Reactjsテーブルに新しい行を追加するために使用しています。何か改善できるかどうか教えてください。

機能状態コンポーネントに新しい要素を追加します。

状態データを定義します。

    const [data, setData] = useState([
        { id: 1, name: 'John', age: 16 },
        { id: 2, name: 'Jane', age: 22 },
        { id: 3, name: 'Josh', age: 21 }
    ]);

ボタンに新しい要素を追加する機能をトリガーさせる

<Button
    // pass the current state data to the handleAdd function so we can append to it.
    onClick={() => handleAdd(data)}>
    Add a row
</Button>
function handleAdd(currentData) {

        // return last data array element
        let lastDataObject = currentTableData[currentTableData.length - 1]

        // assign last elements ID to a variable.
        let lastID = Object.values(lastDataObject)[0] 

        // build a new element with a new ID based off the last element in the array
        let newDataElement = {
            id: lastID + 1,
            name: 'Jill',
            age: 55,
        }

        // build a new state object 
        const newStateData = [...currentData, newDataElement ]

        // update the state
        setData(newStateData);

        // print newly updated state
        for (const element of newStateData) {
            console.log('New Data: ' + Object.values(element).join(', '))
        }

}

-1
this.setState({
  arrayvar: [...this.state.arrayvar, ...newelement]
})

2
この投稿がによる低品質の投稿と見なされないように、説明を追加してくださいstackoverflow
Harsha Biyani

2
@KobeこれはES6で「スプレッドオペレーター」を呼び出し、array2アイテムをarray1に追加します。downvote(:
M.Samiei

@ M.Samiei反対票を避けるために投稿を編集する必要があります。単なるコードスニペットのように低品質です。また、更新はバッチ処理される可能性があるため、この方法で状態を変更することは危険であるため、推奨される方法は、たとえばsetState(state => ({arrayvar: [...state.arrayvar, ...newelement]}) );
NealeU

newelement配列内でオブジェクトを分散すると、TypeErrorとにかくスローされます。
エミールベルジェロン

-1
//------------------code is return in typescript 

const updateMyData1 = (rowIndex:any, columnId:any, value:any) => {

    setItems(old => old.map((row, index) => {
        if (index === rowIndex) {
        return Object.assign(Object.assign({}, old[rowIndex]), { [columnId]: value });
    }
    return row;
}));

-6

このコードは私にとってはうまくいきます:

fetch('http://localhost:8080')
  .then(response => response.json())
  .then(json => {
    this.setState({mystate: this.state.mystate.push.apply(this.state.mystate, json)})
  })

2
それでも、あなたは直接州を変異させます
Asbar Ali

2
また、配列ではなく.push数値を返すため、コンポーネントの状態を正しく更新できません。
エミールベルジェロン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.