Cara memperbarui properti negara bersarang di Bereaksi

390

Saya mencoba mengatur negara saya dengan menggunakan properti bersarang seperti ini:

this.state = {
   someProperty: {
      flag:true
   }
}

Tapi memperbarui keadaan seperti ini,

this.setState({ someProperty.flag: false });

tidak bekerja Bagaimana ini bisa dilakukan dengan benar?

Alex Yong
sumber
4
Apa maksudmu tidak bekerja? Ini bukan pertanyaan yang sangat bagus - apa yang terjadi? Ada kesalahan? Kesalahan apa?
Darren Sweeney
Coba baca: stackoverflow.com/questions/18933985/…
Andrew Paramoshkin
16
Jawabannya adalah Jangan gunakan Nested State dalam React . Baca jawaban yang luar biasa ini .
Qwerty
4
Apa yang seharusnya digunakan?
Jānis Elmeris
42
Cukup tidak menggunakan status bersarang adalah jawaban yang tidak dapat diterima untuk seberapa luas React digunakan saat ini. Situasi ini akan muncul dan pengembang perlu jawaban untuk ini.
serraosays

Jawaban:

456

Untuk setStateobjek bersarang Anda dapat mengikuti pendekatan di bawah ini karena saya pikir setState tidak menangani pembaruan bersarang.

var someProperty = {...this.state.someProperty}
someProperty.flag = true;
this.setState({someProperty})

Idenya adalah untuk membuat objek dummy melakukan operasi di atasnya dan kemudian mengganti status komponen dengan objek yang diperbarui

Sekarang, operator spread hanya membuat salinan level objek bersarang satu tingkat. Jika keadaan Anda sangat bersarang seperti:

this.state = {
   someProperty: {
      someOtherProperty: {
          anotherProperty: {
             flag: true
          }
          ..
      }
      ...
   }
   ...
}

Anda bisa mengaturState menggunakan operator spread di setiap level seperti

this.setState(prevState => ({
    ...prevState,
    someProperty: {
        ...prevState.someProperty,
        someOtherProperty: {
            ...prevState.someProperty.someOtherProperty, 
            anotherProperty: {
               ...prevState.someProperty.someOtherProperty.anotherProperty,
               flag: false
            }
        }
    }
}))

Namun sintaks di atas mendapatkan setiap jelek karena negara menjadi lebih dan lebih bersarang dan karenanya saya sarankan Anda untuk menggunakannya immutability-helper paket untuk memperbarui keadaan.

Lihat jawaban ini tentang cara memperbarui status dengan immutability helper.

Shubham Khatri
sumber
2
@Ohgodwhy, tidak, ini tidak mengakses negara secara langsung sejak {... this.state.someProperty} mengembalikan obanjct baru ke somePropertydan kami memodifikasi itu
Shubham Khatri
4
ini tidak berhasil untuk saya .. saya menggunakan versi reaksi @ 15.6.1
Rajiv
5
@Stophface Kita dapat menggunakan Lodash untuk mengkloning dalam, tapi tidak semua orang akan memasukkan perpustakaan ini hanya untuk mengaturState
Shubham Khatri
15
Tidakkah ini melanggar reactjs.org/docs/… ? Jika dua perubahan pada objek bersarang mendapat batch, perubahan terakhir akan menimpa yang pertama. Jadi cara yang paling benar adalah: this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}}) Agak membosankan ...
olejorgenb
2
Saya tidak berpikir Anda membutuhkan ...prevState,dalam contoh terakhir, Anda hanya somePropertydan anak-anaknya
Jemar Jones
140

Untuk menulisnya dalam satu baris

this.setState({ someProperty: { ...this.state.someProperty, flag: false} });
Yoseph
sumber
12
Disarankan untuk menggunakan updater untuk setState daripada langsung mengakses this.state dalam setState. reactjs.org/docs/react-component.html#setstate
Raghu Teja
6
Saya percaya itu mengatakan, jangan membaca this.statesegera setelah itu setStatedan berharap itu memiliki nilai baru. Versus menelepon di this.statedalam setState. Atau ada juga masalah dengan yang terakhir yang tidak jelas bagi saya?
trpt4him
2
Seperti yang dikatakan trpt4him , tautan yang Anda berikan berbicara tentang masalah mengakses this.statesetelah setState. Selain itu, kita tidak lulus this.stateuntuk setState. The Operator spread properti. Itu sama dengan Object.assign (). github.com/tc39/proposal-object-rest-spread
Yoseph
3
@RaghuTeja mungkin hanya membuat this.setState(currentState => { someProperty: { ...currentState.someProperty, flag: false} });memungkinkan untuk menghindari masalah ini?
Webwoman
Saya telah menambahkan jawaban bagaimana mencapai hal yang sama dengan hook useState untuk kelengkapan.
Andrew
90

Terkadang jawaban langsung bukan yang terbaik :)

Versi pendek:

kode ini

this.state = {
    someProperty: {
        flag: true
    }
}

harus disederhanakan sebagai sesuatu seperti

this.state = {
    somePropertyFlag: true
}

Versi panjang:

Saat ini Anda seharusnya tidak ingin bekerja dengan status bersarang di Bereaksi . Karena Bereaksi tidak berorientasi untuk bekerja dengan negara-negara bersarang dan semua solusi yang diusulkan di sini terlihat sebagai peretasan. Mereka tidak menggunakan kerangka kerja tetapi bertarung dengannya. Mereka menyarankan untuk menulis kode yang tidak begitu jelas untuk tujuan pengelompokan beberapa properti. Jadi mereka sangat menarik sebagai jawaban untuk tantangan tetapi praktis tidak berguna.

Mari kita bayangkan keadaan berikut:

{
    parent: {
        child1: 'value 1',
        child2: 'value 2',
        ...
        child100: 'value 100'
    }
}

Apa yang akan terjadi jika Anda mengubah hanya nilai child1? Bereaksi tidak akan merender ulang tampilan karena menggunakan perbandingan yang dangkal dan akan menemukan ituparent properti tidak berubah. BTW bermutasi objek negara secara langsung dianggap praktik buruk pada umumnya.

Jadi, Anda perlu menciptakan kembali keseluruhan parent objek. Namun dalam hal ini kita akan menemui masalah lain. Bereaksi akan berpikir bahwa semua anak telah mengubah nilai-nilai mereka dan akan membuat ulang semuanya. Tentu saja itu tidak baik untuk kinerja.

Masih mungkin untuk menyelesaikan masalah itu dengan menulis beberapa logika yang rumit, shouldComponentUpdate()tetapi saya lebih suka berhenti di sini dan menggunakan solusi sederhana dari versi singkat.

Konstantin Smolyanin
sumber
4
Saya pikir ada kasus penggunaan di mana keadaan bersarang baik-baik saja. Misalnya negara secara dinamis melacak pasangan nilai kunci. Lihat di sini bagaimana Anda dapat memperbarui negara hanya dengan satu entri di negara bagian: reactjs.org/docs/…
pors
1
Perbandingan dangkal digunakan untuk komponen murni secara default.
Basel Shishani
4
Saya ingin membuat mezbah untuk menghormati Anda dan berdoa setiap pagi. Butuh waktu tiga hari untuk mencapai jawaban ini yang dengan sempurna menjelaskan keputusan desain OBVIOUS. Semua orang hanya mencoba menggunakan operator penyebaran atau melakukan peretasan lainnya hanya karena status bersarang terlihat lebih dapat dibaca manusia.
Edeph
1
Bagaimana Anda menyarankan, untuk menggunakan Bereaksi untuk melakukan hal-hal seperti merender pohon interaktif (misalnya, perusahaan saya memiliki banyak sensor di berbagai belahan dunia, masing-masing dari jenis yang berbeda, dengan sifat yang berbeda, di lokasi yang berbeda (80+ ), dan TENTU SAJA mereka diatur dalam hierarki karena setiap penyebaran menelan biaya ribuan dolar dan merupakan proyek yang lengkap sehingga tidak mungkin untuk mengelolanya tanpa hierarki). Saya cukup ingin tahu tentang bagaimana Anda tidak akan membuat model seperti pohon sebagai ... pohon, di Bereaksi.
DanyAlejandro
10
Sepertinya React tidak dibuat untuk mewakili apa pun yang disimpan sebagai struktur rekursif dengan kedalaman non-sepele. Agak mengecewakan karena tampilan pohon muncul di hampir setiap aplikasi kompleks di luar sana (gmail, manajer file, sistem manajemen dokumen apa pun, ..). Saya telah menggunakan Bereaksi untuk hal-hal ini, tetapi selalu memiliki Bereaksi fanatik memberitahu semua orang bahwa Anda melakukan anti-pola dan menyarankan solusinya adalah menyimpan salinan pohon yang dalam di setiap node (ya, 80 salinan). Saya ingin melihat komponen tampilan pohon non-hacky yang tidak melanggar aturan Bereaksi.
DanyAlejandro
63

Penolakan

Nested State in React adalah desain yang salah

Baca jawaban yang luar biasa ini .

 

Alasan di balik jawaban ini:

SetState React hanyalah kenyamanan bawaan, tetapi Anda segera menyadari bahwa ia memiliki batasnya. Menggunakan properti khusus dan penggunaan forceUpdate yang cerdas memberi Anda lebih banyak. misalnya:

class MyClass extends React.Component {
    myState = someObject
    inputValue = 42
...

MobX, misalnya, benar-benar parit dan menggunakan properti kustom yang dapat diamati.
Gunakan Observable sebagai ganti status dalam komponen Bereaksi.

 


jawaban atas kesengsaraan Anda - lihat contoh di sini

Ada cara lain yang lebih singkat untuk memperbarui properti bersarang apa pun.

this.setState(state => {
  state.nested.flag = false
  state.another.deep.prop = true
  return state
})

Pada satu baris

 this.setState(state => (state.nested.flag = false, state))

catatan: Ini di sini adalah operator Koma ~ MDN , lihatlah dalam aksi di sini (Kotak Pasir) .

Ini mirip dengan (meskipun ini tidak mengubah referensi keadaan)

this.state.nested.flag = false
this.forceUpdate()

Untuk perbedaan halus dalam konteks ini antara forceUpdatedan setStatelihat contoh terkait.

Tentu saja ini menyalahgunakan beberapa prinsip inti, karena stateseharusnya hanya-baca, tetapi karena Anda segera membuang negara lama dan menggantinya dengan negara baru, itu sepenuhnya ok.

Peringatan

Meskipun komponen yang berisi status akan diperbarui dan dirender ulang dengan benar ( kecuali gotcha ini ) , alat peraga akan gagal disebarkan ke anak-anak (lihat komentar Spymaster di bawah) . Gunakan teknik ini hanya jika Anda tahu apa yang Anda lakukan.

Misalnya, Anda dapat melewati prop datar yang diubah yang diperbarui dan dilewati dengan mudah.

render(
  //some complex render with your nested state
  <ChildComponent complexNestedProp={this.state.nested} pleaseRerender={Math.random()}/>
)

Sekarang meskipun referensi untuk complexNestedProp tidak berubah ( shouldComponentUpdate )

this.props.complexNestedProp === nextProps.complexNestedProp

komponen akan dirender ulang setiap kali pembaruan komponen induk, yang merupakan kasus setelah memanggil this.setStateatau this.forceUpdatedi induk.

Efek dari mutasi negara

Menggunakan negara bersarang dan bermutasi negara secara langsung berbahaya karena objek yang berbeda mungkin terus (sengaja atau tidak) yang berbeda referensi (tua) ke negara dan mungkin tidak selalu tahu kapan harus update (misalnya saat menggunakan PureComponentatau jika shouldComponentUpdatediimplementasikan untuk pulang false) OR adalah dimaksudkan untuk menampilkan data lama seperti pada contoh di bawah ini.

Bayangkan sebuah timeline yang seharusnya membuat data historis, mengubah data di bawah tangan akan menghasilkan perilaku yang tidak terduga karena juga akan mengubah item sebelumnya.

aliran negara state-flow-nested

Bagaimanapun di sini Anda dapat melihat bahwa Nested PureChildClassitu tidak dirender karena alat peraga gagal untuk disebarkan.

Qwerty
sumber
12
Anda seharusnya tidak bermutasi saat bereaksi di mana saja. Jika ada komponen bersarang yang menggunakan data dari state.nested atau state.another, mereka tidak akan membuat ulang atau anak-anak mereka. Ini karena objek tidak berubah, sehingga Bereaksi berpikir tidak ada yang berubah.
Zeragamba
@ SpyMaster356 Saya telah memperbarui jawaban saya untuk mengatasi ini. Perhatikan juga bahwa MobX, misalnya, parit statesepenuhnya dan menggunakan properti kustom yang dapat diamati . React's setStatehanyalah kenyamanan bawaan, tetapi Anda segera menyadari bahwa ia memiliki batasnya. Menggunakan properti khusus dan penggunaan cerdas forceUpdatememberi Anda lebih banyak. Gunakan Observable sebagai ganti status dalam komponen Bereaksi.
Qwerty
Saya juga menambahkan contoh react-experiments.herokuapp.com/state-flow (tautan ke git di bagian atas)
Qwerty
Bagaimana jika Anda memuat misalnya objek dengan atribut bersarang dari database ke status? Bagaimana cara menghindari status bersarang dalam kasus itu?
tobiv
3
@tobiv Lebih baik mengubah data saat mengambil menjadi struktur yang berbeda, tapi itu tergantung pada usecase. Tidak apa-apa untuk memiliki objek bersarang atau array objek dalam keadaan jika Anda menganggapnya sebagai unit kompak dari beberapa data. Masalah muncul ketika Anda perlu menyimpan switch UI atau data yang dapat diamati lainnya (yaitu data yang harus segera mengubah tampilan) karena harus datar agar dapat memicu algoritma React diffing internal yang benar. Untuk perubahan pada objek bersarang lainnya, mudah untuk memicu this.forceUpdate()atau mengimplementasikan spesifik shouldComponentUpdate.
Qwerty
21

Jika Anda menggunakan ES2015, Anda memiliki akses ke Object.assign. Anda dapat menggunakannya sebagai berikut untuk memperbarui objek bersarang.

this.setState({
  someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});

Anda menggabungkan properti yang diperbarui dengan yang sudah ada dan menggunakan objek yang dikembalikan untuk memperbarui keadaan.

Sunting: Menambahkan objek kosong sebagai target ke fungsi tetapkan untuk memastikan status tidak dimutasi secara langsung seperti yang ditunjukkan carkod.

Alyssa Roose
sumber
12
Dan saya bisa saja salah tetapi saya tidak berpikir Anda menggunakan Bereaksi tanpa ES2015.
Madbreaks
16
const newState = Object.assign({}, this.state);
newState.property.nestedProperty = "new value";
this.setState(newState);
eyecandy.dev
sumber
1
tampaknya solusi yang lebih elegan di sini, 1) menghindari penyebaran membosankan di dalam setState dan 2) menempatkan negara baru di dalam setState dengan baik, menghindari bermutasi langsung di dalam setState. Last but not least 3) menghindari penggunaan perpustakaan apa pun hanya untuk tugas sederhana
Webwoman
Dua contoh itu tidak setara. Jika propertyberisi beberapa properti bersarang, yang pertama akan menghapusnya, yang kedua tidak. codesandbox.io/s/nameless-pine-42thu
Qwerty
Qwerty, Anda benar, terima kasih atas usahanya. Saya menghapus versi ES6.
eyecandy.dev
Tegas dan sederhana!
Tony Sepia
Saya menggunakan solusi Anda. Keberatan negara saya kecil, dan itu berfungsi dengan baik. Akan Bereaksi membuat hanya untuk negara yang diubah atau akan membuat semuanya?
Kenji Noguchi
14

Ada banyak perpustakaan untuk membantu ini. Misalnya, menggunakan pembantu-immutabilitas :

import update from 'immutability-helper';

const newState = update(this.state, {
  someProperty: {flag: {$set: false}},
};
this.setState(newState);

Menggunakan set lodash / fp :

import {set} from 'lodash/fp';

const newState = set(["someProperty", "flag"], false, this.state);

Menggunakan lodash / fp merge:

import {merge} from 'lodash/fp';

const newState = merge(this.state, {
  someProperty: {flag: false},
});
Tokland
sumber
Jika Anda menggunakan lodash, Anda mungkin ingin mencoba _.cloneDeepdan mengatur status menjadi salinan yang dikloning.
Yixing Liu
@YixingLiu: Saya telah memperbarui jawaban untuk digunakan lodash/fp, jadi kita tidak perlu khawatir tentang mutasi.
tokland
Adakah keuntungan untuk memilih mergeatau setfungsi di lodash / fp selain sintaks?
Toivo Säwén
Jawaban yang bagus, mungkin Anda ingin menambahkan bahwa Anda harus memanggil this.setState(newState)metode lodash / fp juga. Gotcha lain adalah bahwa lodash .set(non-fp) memiliki urutan argumen yang berbeda yang membuat saya tersandung untuk sementara waktu.
Toivo Säwén
7

Kami menggunakan Immer https://github.com/mweststrate/immer untuk menangani masalah seperti ini.

Baru saja mengganti kode ini di salah satu komponen kami

this.setState(prevState => ({
   ...prevState,
        preferences: {
            ...prevState.preferences,
            [key]: newValue
        }
}));

Dengan ini

import produce from 'immer';

this.setState(produce(draft => {
    draft.preferences[key] = newValue;
}));

Dengan immer Anda menangani negara Anda sebagai "objek normal". Keajaiban terjadi di belakang layar dengan objek proxy.

Joakim Jäderberg
sumber
6

Berikut variasi jawaban pertama yang diberikan di utas ini yang tidak memerlukan paket tambahan, pustaka, atau fungsi khusus.

state = {
  someProperty: {
    flag: 'string'
  }
}

handleChange = (value) => {
  const newState = {...this.state.someProperty, flag: value}
  this.setState({ someProperty: newState })
}

Untuk mengatur keadaan bidang bersarang tertentu, Anda telah mengatur seluruh objek. Saya melakukan ini dengan membuat variabel, newStatedan menyebarkan konten dari kondisi saat ini ke dalamnya terlebih dahulu menggunakan operator penyebaran ES2015 . Kemudian, saya mengganti nilai this.state.flagdengan nilai baru (karena saya atur flag: value setelah saya menyebarkan keadaan saat ini ke objek, flagbidang dalam keadaan saat ini ditimpa). Lalu, saya cukup mengatur keadaansomeProperty ke newStateobjek saya .

Matthew Berkompas
sumber
6

Meskipun bersarang tidak benar-benar bagaimana Anda harus memperlakukan keadaan komponen, kadang-kadang untuk sesuatu yang mudah untuk bersarang tingkat tunggal.

Untuk keadaan seperti ini

state = {
 contact: {
  phone: '888-888-8888',
  email: '[email protected]'
 }
 address: {
  street:''
 },
 occupation: {
 }
}

Metode re-useable ive digunakan akan terlihat seperti ini.

handleChange = (obj) => e => {
  let x = this.state[obj];
  x[e.target.name] = e.target.value;
  this.setState({ [obj]: x });
};

lalu hanya memasukkan nama objek untuk setiap sarang yang ingin Anda tuju ...

<TextField
 name="street"
 onChange={handleChange('address')}
 />
pengguna2208124
sumber
4

Saya menggunakan solusi ini.

Jika Anda memiliki status bersarang seperti ini:

   this.state = {
          formInputs:{
            friendName:{
              value:'',
              isValid:false,
              errorMsg:''
            },
            friendEmail:{
              value:'',
              isValid:false,
              errorMsg:''
            }
}

Anda dapat mendeklarasikan fungsi handleChange yang menyalin status saat ini dan menetapkan kembali dengan nilai yang diubah

handleChange(el) {
    let inputName = el.target.name;
    let inputValue = el.target.value;

    let statusCopy = Object.assign({}, this.state);
    statusCopy.formInputs[inputName].value = inputValue;

    this.setState(statusCopy);
  }

di sini html dengan pendengar acara

<input type="text" onChange={this.handleChange} " name="friendName" />
Alberto Piras
sumber
Dikonfirmasi Ini berfungsi baik jika Anda sudah berurusan dengan proyek yang sudah mapan dengan properti bersarang.
metal_jacke1
4

Buat salinan status:

let someProperty = JSON.parse(JSON.stringify(this.state.someProperty))

lakukan perubahan pada objek ini:

someProperty.flag = "false"

sekarang perbarui status

this.setState({someProperty})
Dhakad
sumber
setState tidak sinkron. Jadi sementara itu diperbarui, orang lain dapat memanggil fungsi ini dan menyalin versi negara yang sudah ketinggalan zaman.
Aviv Ben Shabat
3

Meskipun Anda bertanya tentang keadaan komponen Bereaksi berbasis kelas, masalah yang sama ada dengan useState hook. Lebih buruk lagi: hook penggunaanState tidak menerima pembaruan parsial. Jadi pertanyaan ini menjadi sangat relevan ketika useState hook diperkenalkan.

Saya telah memutuskan untuk mengirim jawaban berikut untuk memastikan pertanyaannya mencakup skenario yang lebih modern di mana hook useState digunakan:

Jika Anda punya:

const [state, setState] = useState({ someProperty: { flag: true, otherNestedProp: 1 }, otherProp: 2 })

Anda dapat mengatur properti bersarang dengan mengkloning arus dan menambal segmen data yang diperlukan, misalnya:

setState(current => { ...current, someProperty: { ...current.someProperty, flag: false } });

Atau Anda dapat menggunakan perpustakaan Immer untuk menyederhanakan kloning dan menambal objek.

Atau Anda dapat menggunakan pustaka Hookstate (penafian: Saya seorang penulis) untuk sekadar mengelola data negara (global dan global) yang kompleks sepenuhnya dan meningkatkan kinerja (baca: tidak perlu khawatir tentang rendering optimasi):

import { useStateLink } from '@hookstate/core' 
const state = useStateLink({ someProperty: { flag: true, otherNestedProp: 1 }, otherProp: 2 })

dapatkan bidang untuk dirender:

state.nested.someProperty.nested.flag.get()
// or 
state.get().someProperty.flag

setel bidang bersarang:

state.nested.someProperty.nested.flag.set(false)

Berikut ini adalah contoh Hookstate, di mana status bersarang secara mendalam / berulang dalam struktur data seperti pohon .

Andrew
sumber
2

Dua opsi lain yang belum disebutkan:

  1. Jika Anda memiliki kondisi sarang yang dalam, pertimbangkan apakah Anda dapat merestrukturisasi objek anak untuk duduk di root. Ini membuat data lebih mudah diperbarui.
  2. Ada banyak pustaka praktis yang tersedia untuk menangani status tidak tetap yang tercantum dalam dokumen Redux . Saya merekomendasikan Immer karena memungkinkan Anda untuk menulis kode secara mutatif tetapi menangani kloning yang diperlukan di belakang layar. Ini juga membekukan objek yang dihasilkan sehingga Anda tidak dapat secara tidak sengaja memutasinya nanti.
Rumah Cory
sumber
2

Untuk membuat semuanya menjadi umum, saya mengerjakan jawaban @ ShubhamKhatri dan @ Qwerty.

nyatakan objek

this.state = {
  name: '',
  grandParent: {
    parent1: {
      child: ''
    },
    parent2: {
      child: ''
    }
  }
};

kontrol input

<input
  value={this.state.name}
  onChange={this.updateState}
  type="text"
  name="name"
/>
<input
  value={this.state.grandParent.parent1.child}
  onChange={this.updateState}
  type="text"
  name="grandParent.parent1.child"
/>
<input
  value={this.state.grandParent.parent2.child}
  onChange={this.updateState}
  type="text"
  name="grandParent.parent2.child"
/>

metode updateState

setState sebagai jawaban @ ShubhamKhatri

updateState(event) {
  const path = event.target.name.split('.');
  const depth = path.length;
  const oldstate = this.state;
  const newstate = { ...oldstate };
  let newStateLevel = newstate;
  let oldStateLevel = oldstate;

  for (let i = 0; i < depth; i += 1) {
    if (i === depth - 1) {
      newStateLevel[path[i]] = event.target.value;
    } else {
      newStateLevel[path[i]] = { ...oldStateLevel[path[i]] };
      oldStateLevel = oldStateLevel[path[i]];
      newStateLevel = newStateLevel[path[i]];
    }
  }
  this.setState(newstate);
}

setState sebagai jawaban @ Qwerty

updateState(event) {
  const path = event.target.name.split('.');
  const depth = path.length;
  const state = { ...this.state };
  let ref = state;
  for (let i = 0; i < depth; i += 1) {
    if (i === depth - 1) {
      ref[path[i]] = event.target.value;
    } else {
      ref = ref[path[i]];
    }
  }
  this.setState(state);
}

Catatan: Metode di atas tidak akan berfungsi untuk array

Venugopal
sumber
2

Saya menanggapi dengan sangat serius kekhawatiran yang telah disuarakan untuk membuat salinan lengkap status komponen Anda. Dengan itu, saya akan sangat menyarankan Immer .

import produce from 'immer';

<Input
  value={this.state.form.username}
  onChange={e => produce(this.state, s => { s.form.username = e.target.value }) } />

Ini harus bekerja untuk React.PureComponent(yaitu perbandingan keadaan dangkal oleh Bereaksi) karena secara Immercerdik menggunakan objek proxy untuk secara efisien menyalin pohon keadaan jauh yang sewenang-wenang. Immer juga lebih aman dibandingkan pustaka seperti Immutability Helper, dan sangat ideal untuk pengguna Javascript dan naskah.


Fungsi utilitas typescript

function setStateDeep<S>(comp: React.Component<any, S, any>, fn: (s: 
Draft<Readonly<S>>) => any) {
  comp.setState(produce(comp.state, s => { fn(s); }))
}

onChange={e => setStateDeep(this, s => s.form.username = e.target.value)}
Stephen Paul
sumber
1
stateUpdate = () => {
    let obj = this.state;
    if(this.props.v12_data.values.email) {
      obj.obj_v12.Customer.EmailAddress = this.props.v12_data.values.email
    }
    this.setState(obj)
}
pengguna3444748
sumber
1
Saya tidak berpikir ini benar, karena objhanya referensi ke negara, jadi melalui itu Anda bermutasi this.stateobjek yang sebenarnya
Ciprian Tomoiagă
0

Saya menemukan ini berfungsi untuk saya, memiliki formulir proyek dalam kasus saya di mana misalnya Anda memiliki id, dan nama dan saya lebih suka mempertahankan status untuk proyek bersarang.

return (
  <div>
      <h2>Project Details</h2>
      <form>
        <Input label="ID" group type="number" value={this.state.project.id} onChange={(event) => this.setState({ project: {...this.state.project, id: event.target.value}})} />
        <Input label="Name" group type="text" value={this.state.project.name} onChange={(event) => this.setState({ project: {...this.state.project, name: event.target.value}})} />
      </form> 
  </div>
)

Biarkan aku tahu!

Michael Stokes
sumber
0

Sesuatu seperti ini mungkin sudah cukup,

const isObject = (thing) => {
    if(thing && 
        typeof thing === 'object' &&
        typeof thing !== null
        && !(Array.isArray(thing))
    ){
        return true;
    }
    return false;
}

/*
  Call with an array containing the path to the property you want to access
  And the current component/redux state.

  For example if we want to update `hello` within the following obj
  const obj = {
     somePrimitive:false,
     someNestedObj:{
        hello:1
     }
  }

  we would do :
  //clone the object
  const cloned = clone(['someNestedObj','hello'],obj)
  //Set the new value
  cloned.someNestedObj.hello = 5;

*/
const clone = (arr, state) => {
    let clonedObj = {...state}
    const originalObj = clonedObj;
    arr.forEach(property => {
        if(!(property in clonedObj)){
            throw new Error('State missing property')
        }

        if(isObject(clonedObj[property])){
            clonedObj[property] = {...originalObj[property]};
            clonedObj = clonedObj[property];
        }
    })
    return originalObj;
}

const nestedObj = {
    someProperty:true,
    someNestedObj:{
        someOtherProperty:true
    }
}

const clonedObj = clone(['someProperty'], nestedObj);
console.log(clonedObj === nestedObj) //returns false
console.log(clonedObj.someProperty === nestedObj.someProperty) //returns true
console.log(clonedObj.someNestedObj === nestedObj.someNestedObj) //returns true

console.log()
const clonedObj2 = clone(['someProperty','someNestedObj','someOtherProperty'], nestedObj);
console.log(clonedObj2 === nestedObj) // returns false
console.log(clonedObj2.someNestedObj === nestedObj.someNestedObj) //returns false
//returns true (doesn't attempt to clone because its primitive type)
console.log(clonedObj2.someNestedObj.someOtherProperty === nestedObj.someNestedObj.someOtherProperty) 
Eladian
sumber
0

Saya tahu ini adalah pertanyaan lama tetapi masih ingin membagikan bagaimana saya mencapai ini. Dengan asumsi keadaan dalam konstruktor terlihat seperti ini:

  constructor(props) {
    super(props);

    this.state = {
      loading: false,
      user: {
        email: ""
      },
      organization: {
        name: ""
      }
    };

    this.handleChange = this.handleChange.bind(this);
  }

handleChangeFungsi saya seperti ini:

  handleChange(e) {
    const names = e.target.name.split(".");
    const value = e.target.type === "checkbox" ? e.target.checked : e.target.value;
    this.setState((state) => {
      state[names[0]][names[1]] = value;
      return {[names[0]]: state[names[0]]};
    });
  }

Dan pastikan Anda memberi nama input yang sesuai:

<input
   type="text"
   name="user.email"
   onChange={this.handleChange}
   value={this.state.user.firstName}
   placeholder="Email Address"
/>

<input
   type="text"
   name="organization.name"
   onChange={this.handleChange}
   value={this.state.organization.name}
   placeholder="Organization Name"
/>
Elnoor
sumber
0

Saya melakukan pembaruan bersarang dengan pencarian yang dikurangi :

Contoh:

Variabel bersarang dalam status:

state = {
    coords: {
        x: 0,
        y: 0,
        z: 0
    }
}

Fungsi:

handleChange = nestedAttr => event => {
  const { target: { value } } = event;
  const attrs = nestedAttr.split('.');

  let stateVar = this.state[attrs[0]];
  if(attrs.length>1)
    attrs.reduce((a,b,index,arr)=>{
      if(index==arr.length-1)
        a[b] = value;
      else if(a[b]!=null)
        return a[b]
      else
        return a;
    },stateVar);
  else
    stateVar = value;

  this.setState({[attrs[0]]: stateVar})
}

Menggunakan:

<input
value={this.state.coords.x}
onChange={this.handleTextChange('coords.x')}
/>
Emisael Carrera
sumber
0

Ini adalah status awal saya

    const initialStateInput = {
        cabeceraFamilia: {
            familia: '',
            direccion: '',
            telefonos: '',
            email: ''
        },
        motivoConsulta: '',
        fechaHora: '',
        corresponsables: [],
    }

Kait atau Anda dapat menggantinya dengan negara (komponen kelas)

const [infoAgendamiento, setInfoAgendamiento] = useState(initialStateInput);

Metode untuk handleChange

const actualizarState = e => {
    const nameObjects = e.target.name.split('.');
    const newState = setStateNested(infoAgendamiento, nameObjects, e.target.value);
    setInfoAgendamiento({...newState});
};

Metode untuk mengatur kondisi dengan status bersarang

const setStateNested = (state, nameObjects, value) => {
    let i = 0;
    let operativeState = state;
    if(nameObjects.length > 1){
        for (i = 0; i < nameObjects.length - 1; i++) {
            operativeState = operativeState[nameObjects[i]];
        }
    }
    operativeState[nameObjects[i]] = value;
    return state;
}

Akhirnya ini adalah input yang saya gunakan

<input type="text" className="form-control" name="cabeceraFamilia.direccion" placeholder="Dirección" defaultValue={infoAgendamiento.cabeceraFamilia.direccion} onChange={actualizarState} />
Ramiro Carbonell Delgado
sumber
0

Jika Anda menggunakan formik dalam proyek Anda, ia memiliki beberapa cara mudah untuk menangani hal ini. Inilah cara termudah untuk melakukannya dengan formik.

Pertama atur nilai awal Anda di dalam atribut initivalues ​​formik atau di reaksi. negara

Di sini, nilai awal didefinisikan dalam keadaan bereaksi

   state = { 
     data: {
        fy: {
            active: "N"
        }
     }
   }

tentukan initialValues ​​di atas untuk bidang formik di dalam initiValuesatribut formik

<Formik
 initialValues={this.state.data}
 onSubmit={(values, actions)=> {...your actions goes here}}
>
{({ isSubmitting }) => (
  <Form>
    <Field type="checkbox" name="fy.active" onChange={(e) => {
      const value = e.target.checked;
      if(value) setFieldValue('fy.active', 'Y')
      else setFieldValue('fy.active', 'N')
    }}/>
  </Form>
)}
</Formik>

Buat konsol untuk memeriksa keadaan diperbarui ke dalam stringbukannya fungsi booleanformik setFieldValueuntuk mengatur negara atau pergi dengan alat debugger bereaksi untuk melihat perubahan dalam nilai-nilai status formis iniside.

Sharad Sharma
sumber
0

coba kode ini:

this.setState({ someProperty: {flag: false} });
Soumya
sumber
Tapi ini akan menghapus properti lain yang ada dalam objek yang direferensikan oleh somePropertydan setelah kode di atas dijalankan hanya flagproperti yang akan tetap
Yashas
0

Saya melihat berikut dalam sebuah buku:

this.setState(state => state.someProperty.falg = false);

tapi saya tidak yakin apakah itu benar ..

Malam Dewy
sumber