Modifikasi yang benar dari array negara di ReactJS

416

Saya ingin menambahkan elemen ke akhir a state array, apakah ini cara yang benar untuk melakukannya?

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

Saya khawatir bahwa memodifikasi array di tempat pushdapat menyebabkan masalah - apakah aman?

Alternatif membuat salinan array, dan setStateitu tampak boros.

fadedbee
sumber
9
Saya pikir Anda dapat menggunakan pembantu reaksi kekekalan bereaksi. lihat ini: facebook.github.io/react/docs/update.html#simple-push
nilgun
setState dalam keadaan array periksa solusi saya. <br/> <br> stackoverflow.com/a/59711447/9762736
Rajnikant Lodhi

Jawaban:

763

The React docs mengatakan:

Perlakukan ini. Negara seolah-olah itu tidak berubah.

Anda pushakan mengubah status secara langsung dan yang berpotensi menyebabkan kode rawan kesalahan, bahkan jika Anda "mengatur ulang" status lagi setelahnya. F. mis, itu bisa menyebabkan beberapa metode siklus hidup seperti componentDidUpdatetidak akan memicu.

Pendekatan yang disarankan dalam versi Bereaksi yang berikutnya adalah menggunakan fungsi pembaru ketika memodifikasi kondisi untuk mencegah kondisi balapan:

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

Memori "limbah" bukan masalah dibandingkan dengan kesalahan yang mungkin Anda hadapi menggunakan modifikasi keadaan non-standar.

Sintaks alternatif untuk versi Bereaksi sebelumnya

Anda dapat menggunakan concatuntuk mendapatkan sintaks yang bersih karena mengembalikan array baru:

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

Di ES6 Anda dapat menggunakan Penyebar Operator :

this.setState({
  arrayvar: [...this.state.arrayvar, newelement]
})
David Hellsing
sumber
8
Bisakah Anda memberikan contoh kapan kondisi lomba akan terjadi?
soundly_typed
3
@Qiming pushmengembalikan panjang array baru sehingga tidak akan berfungsi. Juga, setStateasync dan Bereaksi dapat mengantri beberapa perubahan status menjadi satu render pass.
David Hellsing
2
@mindeavor mengatakan Anda memiliki animasiFrame yang mencari parameter di this.state, dan metode lain yang mengubah beberapa parameter lain pada perubahan status. Mungkin ada beberapa frame di mana keadaan telah berubah tetapi tidak tercermin dalam metode yang mendengarkan perubahan, karena setState async.
David Hellsing
1
@ChristopherCamps Jawaban ini tidak mendorong panggilan setStatedua kali, ini menunjukkan dua contoh yang sama dari pengaturan keadaan array tanpa memutasikannya secara langsung.
David Hellsing
2
Cara mudah untuk memperlakukan array status sebagai tidak berubah hari ini adalah: let list = Array.from(this.state.list); list.push('woo'); this.setState({list});Ubah preferensi gaya Anda tentu saja.
basicdays
133

Paling mudah, jika Anda menggunakan ES6.

initialArray = [1, 2, 3];

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

Array baru akan menjadi [1,2,3,4]

untuk memperbarui keadaan Anda di Bereaksi

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

Pelajari lebih lanjut tentang perusakan array

StateLess
sumber
@Muzietto bisa Anda jelaskan?
StateLess
4
Inti dari pertanyaan ini adalah mengubah status Bereaksi, bukan memodifikasi array. Anda melihat poin saya sendiri dan mengedit jawaban Anda. Ini membuat jawaban Anda relevan. Sudah selesai dilakukan dengan baik.
Marco Faustinelli
2
Pertanyaan Anda tidak terkait dengan pertanyaan OP secara langsung
StateLess
1
@ ChanceSmith: itu diperlukan dalam jawaban StateLess juga. Jangan bergantung pada pembaruan negara pada status itu sendiri. Doc resmi: reactjs.org/docs/…
arcol
1
@RayCoder log dan periksa nilai arrayvar, sepertinya bukan array.
StateLess
57

Cara paling sederhana dengan ES6:

this.setState(prevState => ({
    array: [...prevState.array, newElement]
}))
Ridd
sumber
Maaf, dalam kasus saya saya ingin mendorong array ke array. tableData = [['test','test']]Setelah mendorong array baru saya tableData = [['test','test'],['new','new']]. cara mendorong @ David dan @Ridd ini
Johncy
@Johncy Jika Anda ingin [['test','test'],['new','new']]mencoba: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] ] });Ini memasukkan array baru dua kali. this.state.tableData.push([item.student_name,item.homework_status_name,item.comments===null?'-':item.comments]);Ini mencapai hal yang saya inginkan. tapi itu bukan cara yang benar menurut saya.
Johncy
26

Bereaksi dapat mengumpulkan pembaruan, dan oleh karena itu pendekatan yang benar adalah menyediakan setState dengan fungsi yang melakukan pembaruan.

Untuk addon pembaruan Bereaksi, yang berikut ini akan berfungsi:

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

atau untuk concat ():

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

Berikut ini menunjukkan apa yang https://jsbin.com/mofekakuqi/7/edit?js,output sebagai contoh tentang apa yang terjadi jika Anda salah.

Doa setTimeout () menambahkan tiga item dengan benar karena React tidak akan mengelompokkan pembaruan dalam callback setTimeout (lihat https://groups.google.com/d/msg/reactjs/G6pljvpTGX0/0ihYw2zK9dEJ ).

OnClick yang buggy hanya akan menambahkan "Ketiga", tetapi yang tetap, akan menambahkan F, S dan T seperti yang diharapkan.

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'));
NealeU
sumber
Apakah benar ada kasus di mana itu terjadi? Jika kita lakukanthis.setState( update(this.state, {array: {$push: ["First", "Second", "Third"]}}) )
Albizia
1
@ Albizia Saya pikir Anda harus menemukan kolega dan mendiskusikannya dengan mereka. Tidak ada masalah batching jika Anda hanya membuat satu panggilan setState. Intinya adalah untuk menunjukkan bahwa Bereaksi batch pembaruan, jadi ya ... benar-benar ada kasus, yang dapat Anda temukan di versi JSBin dari kode di atas. Hampir semua jawaban di utas ini gagal mengatasi hal ini, sehingga akan ada banyak kode di luar sana yang terkadang salah
NealeU
state.array = state.array.concat([4])ini mengubah objek keadaan sebelumnya.
Emile Bergeron
1
@ EmileBergeron Terima kasih atas ketekunan Anda. Saya akhirnya menoleh ke belakang dan melihat apa yang ditolak otak saya, dan memeriksa dokumen, jadi saya akan mengedit.
NealeU
1
Baik! Sangat mudah untuk salah karena ketidakmampuan dalam JS tidak jelas (bahkan lebih ketika berhadapan dengan API perpustakaan).
Emile Bergeron
17

Seperti @nilgun disebutkan dalam komentar, Anda dapat menggunakan help immutability react . Saya menemukan ini sangat berguna.

Dari dokumen:

Dorong sederhana

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

initialArray masih [1, 2, 3].

Clarkie
sumber
6
Pembantu ketidakberdayaan Bereaksi digambarkan sebagai usang dalam dokumentasi. github.com/kolodny/immutability-helper sekarang seharusnya digunakan.
Periata Breatta
3

Jika Anda menggunakan komponen fungsional, harap gunakan ini seperti di bawah ini.

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
pengguna3444748
sumber
1

Untuk menambahkan elemen baru ke dalam array, push()harus jawabannya.

Untuk menghapus elemen dan memperbarui status array, kode di bawah ini berfungsi untuk saya. splice(index, 1)tidak bisa bekerja.

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);
Jagger
sumber
0

Saya mencoba untuk mendorong nilai dalam keadaan array dan menetapkan nilai seperti ini dan menentukan status array dan nilai push berdasarkan fungsi peta.

 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})
            })
Rajnikant Lodhi
sumber
0

Ini berhasil bagi saya untuk menambahkan array dalam array

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

Inilah contoh 2020, Reactjs Hook yang saya pikir dapat membantu orang lain. Saya menggunakannya untuk menambahkan baris baru ke tabel Reactjs. Beri tahu saya jika saya bisa memperbaiki sesuatu.

Menambahkan elemen baru ke komponen status fungsional:

Tentukan data status:

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

Memiliki tombol memicu fungsi untuk menambahkan elemen baru

<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(', '))
        }

}
Ian Smith
sumber
-1
this.setState({
  arrayvar: [...this.state.arrayvar, ...newelement]
})
M.Samiei
sumber
2
Tambahkan beberapa penjelasan untuk menghindari posting ini untuk dianggap sebagai posting berkualitas rendah oleh stackoverflow.
Harsha Biyani
2
@Kobalah panggilan ini "operator spread" di ES6 dan tambahkan item array2 ke array1. terima kasih untuk downvote (:
M.Samiei
@ M.Samiei Anda perlu mengedit posting Anda untuk menghindari downvotes. Kualitasnya rendah hanya sebagai cuplikan kode. Juga, berbahaya untuk memodifikasi keadaan dengan cara ini karena pembaruan mungkin dikelompokkan, jadi cara yang disukai misalnyasetState(state => ({arrayvar: [...state.arrayvar, ...newelement]}) );
NealeU
dan menyebarkan newelementobjek dalam array melempar aTypeError pula.
Emile Bergeron
-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;
}));
ANUBHAV RAWAT
sumber
-6

Kode ini berfungsi untuk saya:

fetch('http://localhost:8080')
  .then(response => response.json())
  .then(json => {
    this.setState({mystate: this.state.mystate.push.apply(this.state.mystate, json)})
  })
Hamid Hosseinpour
sumber
2
tetap saja, Anda bermutasi secara langsung
Asbar Ali
2
Dan gagal memperbarui status komponen dengan benar karena .pushmengembalikan nomor , bukan array.
Emile Bergeron