Bereaksi: bagaimana cara memperbarui state.item [1] dalam keadaan menggunakan setState?


259

Saya membuat aplikasi tempat pengguna dapat mendesain bentuknya sendiri. Misalnya, tentukan nama bidang dan detail kolom lainnya yang harus dimasukkan.

Komponen tersedia sebagai JSFiddle di sini .

Keadaan awal saya terlihat seperti ini:

var DynamicForm = React.createClass({
  getInitialState: function() {
   var items = {};
   items[1] = { name: 'field 1', populate_at: 'web_start',
                same_as: 'customer_name',
                autocomplete_from: 'customer_name', title: '' };
   items[2] = { name: 'field 2', populate_at: 'web_end',
                same_as: 'user_name', 
                    autocomplete_from: 'user_name', title: '' };

     return { items };
   },

  render: function() {
     var _this = this;
     return (
       <div>
         { Object.keys(this.state.items).map(function (key) {
           var item = _this.state.items[key];
           return (
             <div>
               <PopulateAtCheckboxes this={this}
                 checked={item.populate_at} id={key} 
                   populate_at={data.populate_at} />
            </div>
            );
        }, this)}
        <button onClick={this.newFieldEntry}>Create a new field</button>
        <button onClick={this.saveAndContinue}>Save and Continue</button>
      </div>
    );
  }

Saya ingin memperbarui status ketika pengguna mengubah salah satu nilai, tetapi saya mengalami kesulitan untuk menargetkan objek yang benar:

var PopulateAtCheckboxes = React.createClass({
  handleChange: function (e) {
     item = this.state.items[1];
     item.name = 'newName';
     items[1] = item;
     this.setState({items: items});
  },
  render: function() {
    var populateAtCheckbox = this.props.populate_at.map(function(value) {
      return (
        <label for={value}>
          <input type="radio" name={'populate_at'+this.props.id} value={value}
            onChange={this.handleChange} checked={this.props.checked == value}
            ref="populate-at"/>
          {value}
        </label>
      );
    }, this);
    return (
      <div className="populate-at-checkboxes">
        {populateAtCheckbox}
      </div>
    );
  }
});

Bagaimana saya harus kerajinan this.setStateuntuk memperbaruinya items[1].name?



1
apa jawaban yang Anda pilih untuk pertanyaan ini?
Braian Mellor

Jawaban:


126

Anda bisa menggunakan bantuan updateimmutability untuk ini :

this.setState({
  items: update(this.state.items, {1: {name: {$set: 'updated field name'}}})
})

Atau jika Anda tidak peduli untuk dapat mendeteksi perubahan pada item ini shouldComponentUpdate()menggunakan metode siklus hidup ===, Anda dapat mengedit status secara langsung dan memaksa komponen untuk merender ulang - ini secara efektif sama dengan jawaban @ cahaya lampu, karena ini adalah menarik objek keluar dari keadaan dan mengeditnya.

this.state.items[1].name = 'updated field name'
this.forceUpdate()

Tambahan pasca-edit:

Lihatlah pelajaran Komunikasi Komponen Sederhana dari pelatihan reaksi untuk contoh tentang bagaimana meneruskan fungsi panggilan balik dari induk perusahaan induk ke komponen anak yang perlu memicu perubahan status.


125

Karena ada banyak informasi yang salah di utas ini, inilah cara Anda dapat melakukannya tanpa bantuan libs:

handleChange: function (e) {
    // 1. Make a shallow copy of the items
    let items = [...this.state.items];
    // 2. Make a shallow copy of the item you want to mutate
    let item = {...items[1]};
    // 3. Replace the property you're intested in
    item.name = 'newName';
    // 4. Put it back into our array. N.B. we *are* mutating the array here, but that's why we made a copy first
    items[1] = item;
    // 5. Set the state to our new copy
    this.setState({items});
},

Anda dapat menggabungkan langkah 2 dan 3 jika Anda ingin:

let item = {
    ...items[1],
    name: 'newName'
}

Atau Anda dapat melakukan semuanya dalam satu baris:

this.setState(({items}) => ({
    items: [
        ...items.slice(0,1),
        {
            ...items[1],
            name: 'newName',
        },
        ...items.slice(2)
    ]
}));

Catatan: Saya membuat itemsarray. OP menggunakan suatu objek. Namun, konsepnya sama.


Anda dapat melihat apa yang terjadi di terminal / konsol Anda:

 node
> items = [{name:'foo'},{name:'bar'},{name:'baz'}]
[ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ]
> clone = [...items]
[ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ]
> item1 = {...clone[1]}
{ name: 'bar' }
> item1.name = 'bacon'
'bacon'
> clone[1] = item1
{ name: 'bacon' }
> clone
[ { name: 'foo' }, { name: 'bacon' }, { name: 'baz' } ]
> items
[ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ] // good! we didn't mutate `items`
> items === clone
false // these are different objects
> items[0] === clone[0]
true // we don't need to clone items 0 and 2 because we're not mutating them (efficiency gains!)
> items[1] === clone[1]
false // this guy we copied

4
@TranslucentCloud Oh yeah, metode pembantu sangat bagus, tapi saya pikir semua orang harus tahu apa yang terjadi di bawah tenda :-)
mpen

Mengapa kita perlu langkah 2 dan 3? Mengapa kita tidak bisa memutasi klon setelah langkah pertama secara langsung?
Evmorov

1
@Evmorov Karena itu bukan tiruan yang dalam. Langkah 1 hanya mengkloning array, bukan objek di dalamnya. Dengan kata lain, masing-masing objek di dalam array baru masih "menunjuk ke" objek yang ada di memori - bermutasi satu akan bermutasi yang lain (mereka satu dan sama). Lihat juga items[0] === clone[0]bit dalam contoh terminal saya di bagian bawah. Periksa tiga kali =jika objek merujuk pada hal yang sama.
mpen

Menjadi lebih rumit jika Anda tidak tahu indeks item dalam array.
Ian Warburton

@IanWarburton Tidak juga. items.findIndex()harus membuat karya pendek itu.
mpen

92

Cara yang salah!

handleChange = (e) => {
    const { items } = this.state;
    items[1].name = e.target.value;

    // update state
    this.setState({
        items,
    });
};

Seperti yang ditunjukkan oleh banyak pengembang yang lebih baik di komentar: mengubah keadaan adalah salah!

Butuh waktu beberapa saat untuk memikirkan ini. Di atas bekerja tetapi menghilangkan kekuatan Bereaksi. Misalnya componentDidUpdatetidak akan melihat ini sebagai pembaruan karena langsung dimodifikasi.

Jadi cara yang benar adalah:

handleChange = (e) => {
    this.setState(prevState => ({
        items: {
            ...prevState.items,
            [prevState.items[1].name]: e.target.value,
        },
    }));
};

25
ES6 hanya karena Anda menggunakan "const"?
nkkollaw

49
Bukankah memanggil items[1].role = e.target.valuenegara bermutasi secara langsung?
antony

28
Anda bermutasi negara, ini benar-benar bertentangan dengan gagasan mempertahankan keadaan abadi seperti reaksi yang disarankan. Ini dapat memberi Anda banyak rasa sakit dalam aplikasi besar.
ncubica

8
@MarvinVK, jawaban Anda mengatakan "Jadi praktik terbaik adalah:" diikuti dengan penggunaan "this.forceUpdate ();" yang tidak direkomendasikan karena dapat ditimpa oleh setState (), lihat facebook.github.io/react/docs/react-component.html#state . Terbaik untuk mengubah ini sehingga tidak membingungkan pembaca di masa depan.
James Z.

8
Hanya ingin menunjukkan bahwa banyak komentar tidak relevan sekarang karena pengeditan dan cara yang benar sebenarnya adalah cara yang benar.
heez

50

Untuk memodifikasi objek / variabel yang bersarang dalam kondisi React, biasanya tiga metode digunakan: JavaScript vanilla Object.assign, immutability-helper dan cloneDeepdari Lodash .

Ada juga banyak lib pihak ketiga lain yang kurang populer untuk mencapai ini, tetapi dalam jawaban ini, saya akan membahas hanya tiga opsi ini. Juga, beberapa metode JavaScript vanilla tambahan ada, seperti penyebaran array, (lihat jawaban @ mpen misalnya), tetapi mereka tidak terlalu intuitif, mudah digunakan dan mampu menangani semua situasi manipulasi negara.

Seperti yang ditunjukkan berkali-kali dalam komentar-komentar teratas atas jawaban, yang penulisnya mengusulkan mutasi langsung negara: jangan lakukan itu . Ini adalah anti-pola Bereaksi di mana-mana, yang pasti akan mengakibatkan konsekuensi yang tidak diinginkan. Pelajari cara yang benar.

Mari kita bandingkan tiga metode yang banyak digunakan.

Diberikan struktur objek keadaan ini:

state = {
    outer: {
        inner: 'initial value'
    }
}

Anda dapat menggunakan metode berikut untuk memperbarui nilai innerbidang paling dalam tanpa mempengaruhi bagian lain negara.

1. Object.assign JavaScript milik Vanilla

const App = () => {
  const [outer, setOuter] = React.useState({ inner: 'initial value' })

  React.useEffect(() => {
    console.log('Before the shallow copying:', outer.inner) // initial value
    const newOuter = Object.assign({}, outer, { inner: 'updated value' })
    console.log('After the shallow copy is taken, the value in the state is still:', outer.inner) // initial value
    setOuter(newOuter)
  }, [])

  console.log('In render:', outer.inner)

  return (
    <section>Inner property: <i>{outer.inner}</i></section>
  )
}

ReactDOM.render(
  <App />,
  document.getElementById('react')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>

<main id="react"></main>

Ingat, Object.assign itu itu tidak akan melakukan kloning yang dalam , karena hanya menyalin nilai properti , dan itulah sebabnya apa yang dilakukannya disebut penyalinan dangkal (lihat komentar).

Agar ini berfungsi, kita hanya boleh memanipulasi properti tipe primitif ( outer.inner), yaitu string, angka, boolean.

Dalam contoh ini, kami membuat konstanta baru ( const newOuter...), menggunakan Object.assign, yang membuat objek kosong ( {}), salinanouter objek ( { inner: 'initial value' }) ke dalamnya dan kemudian menyalin objek yang berbeda { inner: 'updated value' } di atasnya.

Dengan cara ini, pada akhirnya baru dibuat newOuter konstanta yang akan menyimpan nilai { inner: 'updated value' }sejak innerproperti ditimpa. Ini newOuteradalah objek baru, yang tidak ditautkan ke objek dalam keadaan, sehingga dapat dimutasi sesuai kebutuhan dan negara akan tetap sama dan tidak berubah sampai perintah untuk memperbaruinya dijalankan.

Bagian terakhir adalah menggunakan setOuter() setter untuk menggantikan yang asli outerdi negara bagian dengan objek yang baru dibuat newOuter(hanya nilainya yang akan berubah, nama properti outertidak akan).

Sekarang bayangkan kita memiliki keadaan yang lebih dalam seperti state = { outer: { inner: { innerMost: 'initial value' } } }. Kita bisa mencoba membuat newOuterobjek dan mengisinya dengan outerkonten dari negara, tetapiObject.assign tidak akan dapat menyalin innerMostnilai ke objek yang baru dibuat ini newOuterkarena innerMostbersarang terlalu dalam.

Anda masih bisa menyalin inner, seperti pada contoh di atas, tetapi karena sekarang objek dan bukan primitif, referensi darinewOuter.inner akan disalin ke outer.innersebaliknya, yang berarti bahwa kita akan berakhir dengan newOuterobjek lokal yang secara langsung terkait dengan objek di negara bagian .

Itu berarti bahwa dalam hal ini mutasi yang dibuat secara lokal newOuter.inner akan secara langsung mempengaruhi outer.innerobjek (dalam keadaan), karena mereka sebenarnya menjadi hal yang sama (dalam memori komputer).

Object.assign karena itu hanya akan berfungsi jika Anda memiliki struktur keadaan dalam satu tingkat yang relatif sederhana dengan anggota terdalam yang memegang nilai dari tipe primitif.

Jika Anda memiliki objek yang lebih dalam (tingkat 2 atau lebih), yang harus Anda perbarui, jangan gunakan Object.assign . Anda berisiko mengalami mutasi secara langsung.

2. KlonDeep Lodash

const App = () => {
  const [outer, setOuter] = React.useState({ inner: 'initial value' })

  React.useEffect(() => {
    console.log('Before the deep cloning:', outer.inner) // initial value
    const newOuter = _.cloneDeep(outer) // cloneDeep() is coming from the Lodash lib
    newOuter.inner = 'updated value'
    console.log('After the deeply cloned object is modified, the value in the state is still:', outer.inner) // initial value
    setOuter(newOuter)
  }, [])

  console.log('In render:', outer.inner)

  return (
    <section>Inner property: <i>{outer.inner}</i></section>
  )
}

ReactDOM.render(
  <App />,
  document.getElementById('react')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

<main id="react"></main>

CloneDeep Lodash jauh lebih mudah digunakan. Ini melakukan kloning yang mendalam , jadi ini adalah opsi yang kuat, jika Anda memiliki keadaan yang cukup kompleks dengan objek multi-level atau array di dalamnya. HanyacloneDeep() properti negara tingkat atas, mutasikan bagian yang dikloning dengan cara apa pun yang Anda suka, dan setOuter()kembali ke negara bagian.

3. penolong kekal

const App = () => {
  const [outer, setOuter] = React.useState({ inner: 'initial value' })
  
  React.useEffect(() => {
    const update = immutabilityHelper
    console.log('Before the deep cloning and updating:', outer.inner) // initial value
    const newOuter = update(outer, { inner: { $set: 'updated value' } })
    console.log('After the cloning and updating, the value in the state is still:', outer.inner) // initial value
    setOuter(newOuter)
  }, [])

  console.log('In render:', outer.inner)

  return (
    <section>Inner property: <i>{outer.inner}</i></section>
  )
}

ReactDOM.render(
  <App />,
  document.getElementById('react')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
<script src="https://wzrd.in/standalone/immutability-helper@3.0.0"></script>

<main id="react"></main>

immutability-helpermembawanya ke tingkat yang baru, dan hal yang keren tentang hal itu adalah bahwa hal itu tidak hanya dapat $setnilai untuk item negara, tetapi juga $push, $splice, $merge(dll) mereka. Berikut adalah daftar perintah tersedia.

Catatan samping

Sekali lagi, perlu diingat, itu setOuterhanya memodifikasi properti tingkat pertama objek negara ( outerdalam contoh ini), bukan yang bersarang secara mendalam (outer.inner ). Jika berperilaku dengan cara yang berbeda, pertanyaan ini tidak akan ada.

Mana yang tepat untuk proyek Anda?

Jika Anda tidak ingin atau tidak dapat menggunakan dependensi eksternal , dan memiliki struktur keadaan yang sederhana , patuhi Object.assign.

Jika Anda memanipulasi kondisi besar dan / atau rumit , Lodash'scloneDeep adalah pilihan yang bijaksana.

Jika Anda memerlukan kemampuan tingkat lanjut , yaitu jika struktur negara Anda kompleks dan Anda perlu melakukan semua jenis operasi di atasnya, cobalahimmutability-helper , itu adalah alat yang sangat canggih yang dapat digunakan untuk manipulasi negara.

... atau, apakah Anda benar - benar perlu melakukan ini sama sekali?

Jika Anda menyimpan data yang kompleks dalam status React, mungkin ini saat yang tepat untuk memikirkan cara lain untuk menanganinya. Mengatur objek keadaan kompleks tepat di komponen Bereaksi bukanlah operasi langsung, dan saya sangat menyarankan untuk memikirkan pendekatan yang berbeda.

Kemungkinan besar Anda lebih baik menyimpan data kompleks Anda di toko Redux, mengaturnya di sana menggunakan reduksi dan / atau kisah-kisah dan mengaksesnya menggunakan penyeleksi.


Object.assigntidak melakukan salinan yang dalam. Katakan, jika a = {c: {d: 1}}dan b = Object.assign({}, a), maka Anda jalankan b.c.d = 4, maka a.c.ddimutasi.
Awol

Anda benar, nilai 1objek paling dalam ( a.c.d) akan dimutasi. Tetapi jika Anda akan menugaskan kembali penerus tingkat pertama b, seperti ini b.c = {f: 1}:, bagian yang sesuai dari atidak akan termutasi (itu akan tetap {d: 1}). Tangkapan yang bagus, saya akan segera memperbarui jawabannya.
Neurotransmitter

Apa yang telah Anda tetapkan sebenarnya a shallow copydan bukan a deep copy. Sangat mudah untuk membingungkan apa shallow copyartinya. Dalam shallow copy,, a !== btetapi untuk setiap kunci dari objek sumber a,a[key] === b[key]
Awol

Ya, secara eksplisit disebutkan kedangkalan Object.assigndalam jawaban.
Neurotransmitter

JSON.parse(JSON.stringify(object))juga merupakan varian untuk klon dalam. Performanya lebih buruk dari biasanya cloneDeep. Measethat.net/Benchmarks/Show/2751/0/…
tylik

35

Saya memiliki masalah yang sama. Inilah solusi sederhana yang berfungsi!

const newItems = [...this.state.items];
newItems[item] = value;
this.setState({ items:newItems });

11
@TranslucentCloud - ini pasti bukan mutasi langsung. array asli dikloning, dimodifikasi dan kemudian negara diatur lagi menggunakan Array dikloning.
vsync

@vsync ya, sekarang setelah Anda mengedit jawaban asli, ini bukan mutasi sama sekali.
Neurotransmitter

2
@TranslucentCloud - Sebelumnya, edit saya tidak ada hubungannya dengan itu, terima kasih banyak atas sikapnya. @Jonas di sini hanya membuat kesalahan sederhana dalam jawabannya, menggunakan {kurung kurawal alih-alih kurung yang telah saya perbaiki
vsync

31

Menurut dokumentasi Bereaksi pada setState , menggunakan Object.assignseperti yang disarankan oleh jawaban lain di sini tidak ideal. Karena sifatsetState perilaku sinkron, panggilan berikutnya menggunakan teknik ini dapat menggantikan panggilan sebelumnya yang menyebabkan hasil yang tidak diinginkan.

Sebagai gantinya, Bereaksi dokumen merekomendasikan untuk menggunakan bentuk pembaru setStateyang beroperasi pada keadaan sebelumnya. Ingatlah bahwa ketika memperbarui array atau objek, Anda harus mengembalikan array atau objek baru karena Bereaksi mengharuskan kami untuk mempertahankan keabadian. Menggunakan operator sintaksis ES6 untuk menyalin array, membuat atau memperbarui properti objek pada indeks array yang diberikan akan terlihat seperti ini:

this.setState(prevState => {
    const newItems = [...prevState.items];
    newItems[index].name = newName;
    return {items: newItems};
})

2
Ini jawaban yang tepat jika Anda menggunakan ES6. Ini sesuai dengan apa yang dijawab @Jonas. Namun karena penjelasan itu menonjol.
Sourabh

Ya kode ini bekerja dengan sangat baik dan sangat sederhana ..
Shoeb Mirza

29

Pertama dapatkan item yang Anda inginkan, ubah apa yang Anda inginkan pada objek itu dan atur kembali pada status. Cara Anda menggunakan status dengan hanya mengirimkan objek getInitialStateakan jauh lebih mudah jika Anda menggunakan objek yang dikunci.

handleChange: function (e) {
   item = this.state.items[1];
   item.name = 'newName';
   items[1] = item;

   this.setState({items: items});
}

3
Nah, itu menghasilkan Uncaught TypeError: Cannot read property 'items' of null.
Martin

Tidak, tidak akan. Kesalahan Anda kemungkinan besar berasal dari cara Anda melakukannya getInitialState.
Henrik Andersson

10
@ HenrikAndersson Sesuatu yang tidak beres dalam contoh Anda. itemstidak didefinisikan di mana pun.
Edward D'Souza

1
@ EdwardD'Souza Anda benar sekali! Jawaban saya adalah menunjukkan bagaimana seharusnya didefinisikan dan digunakan. Cara kode penanya diatur tidak berfungsi untuk hal yang ia inginkan, oleh karena itu diperlukannya objek kunci.
Henrik Andersson

7
Ini adalah khas anti-pola Bereaksi, Anda memberikan itemreferensi ke this.state.items[1]variabel negara sendiri . Kemudian Anda memodifikasi item( item.name = 'newName'), dan dengan demikian bermutasi secara langsung, yang sangat tidak disarankan. Dalam contoh Anda, bahkan tidak perlu menelepon this.setState({items: items}), karena status sudah dimutasi secara langsung.
Neurotransmitter

22

Jangan bermutasi di tempat. Ini dapat menyebabkan hasil yang tidak terduga. Saya telah belajar pelajaran saya! Selalu bekerja dengan salinan / klon, Object.assign()itu bagus:

item = Object.assign({}, this.state.items[1], {name: 'newName'});
items[1] = item;
this.setState({items: items});

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign


8
apa yang ada itemsdalam contoh Anda? Apakah maksud Anda this.state.itemsatau sesuatu yang lain?
Buh Buh

Saya menguji ini tetapi dia kehilangan satu baris. Di atas items[1] = item;harus ada kalimat yang mengatakan items = this.state.items;. Waspadalah javascript saya berkarat dan saya belajar bereaksi untuk proyek rumah saya jadi saya tidak tahu apakah ini baik atau buruk :-)
Greg0ry

4

Sangat sederhana.

Pertama-tama tarik objek seluruh item dari keadaan, perbarui bagian dari objek item seperti yang diinginkan, dan kembalikan seluruh objek item ke keadaan melalui setState.

handleChange: function (e) {
  items = Object.assign(this.state.items); // Pull the entire items object out. Using object.assign is a good idea for objects.
  items[1].name = 'newName'; // update the items object as needed
  this.setState({ items }); // Put back in state
}

"Object.assign akan berfungsi jika Anda memiliki struktur keadaan dalam satu tingkat yang relatif sederhana dengan anggota terdalam yang memegang nilai tipe primitif".
Neurotransmitter

3

Karena tidak ada opsi di atas yang ideal untuk saya, saya akhirnya menggunakan peta:

this.setState({items: this.state.items.map((item,idx)=> idx!==1 ?item :{...item,name:'new_name'}) })

2

Mutasi gratis:

// given a state
state = {items: [{name: 'Fred', value: 1}, {name: 'Wilma', value: 2}]}

// This will work without mutation as it clones the modified item in the map:
this.state.items
   .map(item => item.name === 'Fred' ? {...item, ...{value: 3}} : item)

this.setState(newItems)

2
Saya tidak bisa melihat di mana newItemsdiatur.
Neurotransmitter

Bukankah peta plus perbandingan dalam array mengerikan untuk kinerja?
Natassia Tavares

@NatassiaTavares .. apa? mungkin Anda bingung fo ofatau forEachpeta adalah yang tercepat.
Deano

1

Ternyata ini sangat sulit dan tidak ada satupun dari ES6 yang menyebar yang berfungsi seperti yang diharapkan. Sedang menggunakan struktur seperti ini untuk mendapatkan properti elemen yang diberikan untuk keperluan tata letak.

ditemukan menggunakan updatemetode dari immutability-helpermenjadi yang paling lurus ke depan dalam contoh sederhana ini:

constructor(props) {
    super(props)
    this.state = { values: [] }
    this.updateContainerState = this.updateContainerState.bind(this)
  }

updateContainerState(index, value) {
    this.setState((state) => update(state, { values: { [index]: { $set: value } } }))
  }

sebagai diadaptasi dari https://github.com/kolodny/immutability-helper#computed-property-names

dari anggota array yang diperbarui adalah objek yang lebih kompleks menggunakan metode deep copy yang sesuai berdasarkan kompleksitas.

Tentunya ada cara yang lebih baik untuk menangani parameter tata letak, tetapi ini tentang cara menangani array. Nilai-nilai yang relevan untuk setiap elemen anak juga dapat dihitung di luarnya, tetapi saya merasa lebih nyaman untuk melewatkan containerState ke bawah, sehingga mereka dapat mengambil sifat anak-anak sesuka mereka dan Perbarui susunan status induk pada indeks yang diberikan.

import React from 'react'
import update from 'immutability-helper'
import { ContainerElement } from './container.component.style.js'
import ChildComponent from './child-component'
export default class ContainerComponent extends React.Component {
  constructor(props) {
    super(props)
    this.state = { values: [] }
    this.updateContainerState = this.updateContainerState.bind(this)
  }

  updateContainerState(index, value) {
    this.setState((state) => update(state, { values: { [index]: { $set: value } } }))
  }

  // ...

  render() {
    let index = 0
    return (
      <ContainerElement>
      <ChildComponent
        index={index++}
        containerState={this.state}
        updateContainerState={this.updateContainerState}
      />
      <ChildComponent
        index={index++}
        containerState={this.state}
        updateContainerState={this.updateContainerState}
      />
      </ContainerElement>
    )
  }
}

1

Gunakan peta larik dengan fungsi panah, dalam satu baris

this.setState({
    items: this.state.items.map((item, index) =>
      index === 1 ? { ...item, name: 'newName' } : item,
   )
})

1
Ini pada dasarnya identik dengan jawaban lain yang diposting sekitar setahun yang lalu
Kinerja Tertentu

0

Gunakan acara ini handleChangeuntuk mengetahui elemen yang telah berubah dan kemudian memperbaruinya. Untuk itu, Anda mungkin perlu mengubah beberapa properti untuk mengidentifikasi dan memperbaruinya.

Lihat biola https://jsfiddle.net/69z2wepo/6164/


0

Saya akan memindahkan fungsi menangani perubahan dan menambahkan parameter indeks

handleChange: function (index) {
    var items = this.state.items;
    items[index].name = 'newName';
    this.setState({items: items});
},

ke komponen formulir Dinamis dan meneruskannya ke komponen PopulateAtCheckboxes sebagai prop. Saat Anda mengulang item, Anda dapat menyertakan penghitung tambahan (disebut indeks dalam kode di bawah) untuk diteruskan ke perubahan pegangan seperti yang ditunjukkan di bawah ini

{ Object.keys(this.state.items).map(function (key, index) {
var item = _this.state.items[key];
var boundHandleChange = _this.handleChange.bind(_this, index);
  return (
    <div>
        <PopulateAtCheckboxes this={this}
            checked={item.populate_at} id={key} 
            handleChange={boundHandleChange}
            populate_at={data.populate_at} />
    </div>
);
}, this)}

Akhirnya Anda dapat memanggil pendengar perubahan Anda seperti yang ditunjukkan di bawah ini di sini

<input type="radio" name={'populate_at'+this.props.id} value={value} onChange={this.props.handleChange} checked={this.props.checked == value} ref="populate-at"/>

Jangan pernah mengubah status Bereaksi secara langsung.
Neurotransmitter

0

Jika Anda perlu mengubah hanya bagian dari Array, Anda memiliki komponen reaksi dengan status diatur ke.

state = {items: [{name: 'red-one', value: 100}, {name: 'green-one', value: 999}]}

Yang terbaik adalah memperbarui red-onedi Arraysebagai berikut:

const itemIndex = this.state.items.findIndex(i=> i.name === 'red-one');
const newItems = [
   this.state.items.slice(0, itemIndex),
   {name: 'red-one', value: 666},
   this.state.items.slice(itemIndex)
]

this.setState(newItems)

apa newArray? maksudmu newItems? Jika Anda melakukannya, bukankah itu akan meninggalkan negara bagian dengan hanya satu item sesudahnya?
micnil

Ini akan memperkenalkan properti baru newItemske stateobjek, dan tidak akan memperbarui itemsproperti yang ada .
Neurotransmitter

0

atau jika Anda memiliki daftar yang dibuat secara dinamis dan Anda tidak tahu indeks tetapi hanya memiliki kunci atau id:

let ItemsCopy = []
let x = this.state.Items.map((entry) =>{

    if(entry.id == 'theIDYoureLookingFor')
    {
        entry.PropertyToChange = 'NewProperty'
    }

    ItemsCopy.push(entry)
})


this.setState({Items:ItemsCopy});

0

Coba dengan kode:

this.state.items[1] = 'new value';
var cloneObj = Object.assign({}, this.state.items);

this.setState({items: cloneObj });

0

Mengikuti sepotong kode mudah di otak saya yang membosankan. Menghapus objek dan mengganti dengan yang diperbarui

    var udpateditem = this.state.items.find(function(item) { 
                   return item.name == "field_1" });
    udpateditem.name= "New updated name"                       
    this.setState(prevState => ({                                   
    items:prevState.dl_name_template.filter(function(item) { 
                                    return item.name !== "field_1"}).concat(udpateditem)
    }));

0

Bagaimana dengan membuat komponen lain (untuk objek yang perlu masuk ke array) dan meneruskan yang berikut ini sebagai alat peraga?

  1. indeks komponen - indeks akan digunakan untuk membuat / memperbarui dalam array.
  2. set function - Fungsi ini memasukkan data ke dalam array berdasarkan indeks komponen.
<SubObjectForm setData={this.setSubObjectData}                                                            objectIndex={index}/>

Di sini {index} dapat diteruskan berdasarkan posisi di mana SubObjectForm ini digunakan.

dan setSubObjectData dapat berupa sesuatu seperti ini.

 setSubObjectData: function(index, data){
      var arrayFromParentObject= <retrieve from props or state>;
      var objectInArray= arrayFromParentObject.array[index];
      arrayFromParentObject.array[index] = Object.assign(objectInArray, data);
 }

Dalam SubObjectForm, this.props.setData dapat dipanggil pada perubahan data seperti yang diberikan di bawah ini.

<input type="text" name="name" onChange={(e) => this.props.setData(this.props.objectIndex,{name: e.target.value})}/>

0
this.setState({
      items: this.state.items.map((item,index) => {
        if (index === 1) {
          item.name = 'newName';
        }
        return item;
      })
    });

1
ini sangat tidak optimal, Anda mengulang seluruh array untuk memperbarui hanya item kedua?
wscourge

Anda juga memutasikan elemen dalam array pertama juga, Anda harus menggunakanitem = Object.assign({}, item, {name: 'newName'});
remram

0

@ JonnyBuchanan jawaban berfungsi dengan baik, tetapi hanya variabel status array. Jika variabel keadaan hanya berupa kamus tunggal, ikuti ini:

inputChange = input => e => {
    this.setState({
        item: update(this.state.item, {[input]: {$set: e.target.value}})
    })
}

Anda dapat mengganti [input]dengan nama bidang kamus Anda dan e.target.valuenilainya. Kode ini melakukan pekerjaan pembaruan pada acara perubahan input formulir saya.


-3

Coba ini pasti akan berhasil, kasus lain saya mencoba tetapi tidak berhasil

import _ from 'lodash';

this.state.var_name  = _.assign(this.state.var_name, {
   obj_prop: 'changed_value',
});

Jangan pernah mengubah status Bereaksi secara langsung.
Neurotransmitter

-3
 handleChanges = (value, key) => {
     // clone the current State object
    let cloneObject = _.extend({}, this.state.currentAttribute);
    // key as user.name and value= "ABC" then current attributes have current properties as we changes
    currentAttribute[key] = value;
    // then set the state "currentAttribute" is key and "cloneObject" is changed object.  
    this.setState({currentAttribute: cloneObject});

dan Ubah dari kotak Teks tambahkan event onChange

onChange = {
   (event) => {                                                
      this.handleChanges(event.target.value, "title");
   }
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.