Aktualizowanie obiektu za pomocą setState w React

265

Czy w ogóle można aktualizować właściwości obiektu setState?

Coś jak:

this.state = {
   jasper: { name: 'jasper', age: 28 },
}

Próbowałem:

this.setState({jasper.name: 'someOtherName'});

i to:

this.setState({jasper: {name: 'someothername'}})

Pierwszy powoduje błąd składniowy, a drugi po prostu nic nie robi. Jakieś pomysły?

JohnSnow
źródło
5
drugi kod działałby, ale utraciłbyś agewłaściwość w środku jasper.
giorgim,

Odpowiedzi:

576

Istnieje wiele sposobów, aby to zrobić, ponieważ zmiana stanu jest praca asynchroniczny , tak aby zaktualizować stan obiektu, musimy użyć Updater funkcji z setState.

1- najprostszy:

Najpierw utwórz kopię, jaspera następnie wykonaj następujące zmiany:

this.setState(prevState => {
  let jasper = Object.assign({}, prevState.jasper);  // creating copy of state variable jasper
  jasper.name = 'someothername';                     // update the name property, assign a new value                 
  return { jasper };                                 // return new object jasper object
})

Zamiast używać Object.assignmożemy również napisać to w ten sposób:

let jasper = { ...prevState.jasper };

2- Korzystanie z operatora rozrzucania :

this.setState(prevState => ({
    jasper: {                   // object that we want to update
        ...prevState.jasper,    // keep all other key-value pairs
        name: 'something'       // update the value of specific key
    }
}))

Uwaga: Object.assign i Spread Operatortworzy tylko płytką kopię , więc jeśli zdefiniowałeś zagnieżdżony obiekt lub tablicę obiektów, potrzebujesz innego podejścia.


Aktualizowanie zagnieżdżonego obiektu stanu:

Załóżmy, że zdefiniowałeś stan jako:

this.state = {
  food: {
    sandwich: {
      capsicum: true,
      crackers: true,
      mayonnaise: true
    },
    pizza: {
      jalapeno: true,
      extraCheese: false
    }
  }
}

Aby zaktualizować obiekt extraCheese pizzy:

this.setState(prevState => ({
  food: {
    ...prevState.food,           // copy all other key-value pairs of food object
    pizza: {                     // specific object of food object
      ...prevState.food.pizza,   // copy all pizza key-value pairs
      extraCheese: true          // update value of specific key
    }
  }
}))

Aktualizowanie tablicy obiektów:

Załóżmy, że masz aplikację do zrobienia i zarządzasz danymi w tej formie:

this.state = {
  todoItems: [
    {
      name: 'Learn React Basics',
      status: 'pending'
    }, {
      name: 'Check Codebase',
      status: 'pending'
    }
  ]
}

Aby zaktualizować status dowolnego obiektu do zrobienia, uruchom mapę w tablicy i sprawdź, czy dla każdego obiektu jest jakaś unikalna wartość, w przypadku condition=truezwrócenia nowego obiektu ze zaktualizowaną wartością, w innym przypadku tego samego obiektu.

let key = 2;
this.setState(prevState => ({

  todoItems: prevState.todoItems.map(
    el => el.key === key? { ...el, status: 'done' }: el
  )

}))

Sugestia: Jeśli obiekt nie ma unikalnej wartości, użyj indeksu tablicy.

Mayank Shukla
źródło
Jednak sposób, w jaki próbowałem, teraz działa ... drugi sposób: S
JohnSnow
7
@JohnSnow w tobie drugi usunie inne właściwości z jasperobiektu, czy console.log(jasper)zobaczysz tylko jeden klucz name, wiek nie będzie tam :)
Mayank Shukla
1
@JohnSnow, tak, ta metoda jest właściwa, jeśli jasperjest object, czy nie jest najlepsza czy nie, mogą być możliwe inne lepsze rozwiązania :)
Mayank Shukla
6
Warto tutaj wspomnieć, że ani Object.assignnie kopiują właściwości głębokiego kopiowania przez operatora. W ten sposób powinieneś zastosować obejścia takie jak lodash deepCopyitp.
Alex Vasilev
1
Jak się masz let { jasper } = this.state?
Brian Le
37

Jest to najszybszy i najbardziej czytelny sposób:

this.setState({...this.state.jasper, name: 'someothername'});

Nawet jeśli this.state.jasperjuż zawiera właściwość name, name: 'someothername'należy użyć nowej nazwy .

mannok
źródło
38
To rozwiązanie ma jedną dużą wadę: w celu zoptymalizowania aktualizacji stanu React może grupować wiele aktualizacji. W konsekwencji this.state.jasperniekoniecznie zawiera najnowszy stan. Lepiej użyj notacji z prevState.
zgrzeszyli
33

Użyj operatora rozprzestrzeniania i trochę ES6 tutaj

this.setState({
    jasper: {
          ...this.state.jasper,
          name: 'something'
    }
})
Nikhil
źródło
To aktualizuje jaspis, ale inne właściwości są usuwane ze stanu.
iaq
11

Użyłem tego rozwiązania.

Jeśli masz taki stan zagnieżdżenia:

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

możesz zadeklarować funkcję handleChange, która kopiuje bieżący status i przypisuje go ponownie ze zmienionymi wartościami

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);
  }

tutaj HTML z detektorem zdarzeń. Upewnij się, że używasz tej samej nazwy, co nazwa obiektu stanu (w tym przypadku „friendName”)

<input type="text" onChange={this.handleChange} " name="friendName" />
Alberto Piras
źródło
To działało dla mnie, statusCopy.formInputs[inputName] = inputValue;
tyle
9

Wiem, że jest tu wiele odpowiedzi, ale jestem zaskoczony, że żadna z nich nie tworzy kopii nowego obiektu poza setState, a następnie po prostu setState ({newObject}). Czysty, zwięzły i niezawodny. Więc w tym przypadku:

const jasper = { ...this.state.jasper, name: 'someothername' }
this.setState(() => ({ jasper }))

Lub dla właściwości dynamicznej (bardzo przydatne w przypadku formularzy)

const jasper = { ...this.state.jasper, [VarRepresentingPropertyName]: 'new value' }
this.setState(() => ({ jasper }))

Colemerrick
źródło
7

spróbuj tego, powinno działać dobrze

this.setState(Object.assign(this.state.jasper,{name:'someOtherName'}));
Burak Kahraman
źródło
4
Mutujesz bezpośrednio obiekt stanu. Aby skorzystać z tego podejścia, dodaj nowy obiekt jako źródło:Object.assign({}, this.state.jasper, {name:'someOtherName'})
Albizia
3

Pierwszy przypadek to rzeczywiście błąd składniowy.

Ponieważ nie widzę reszty twojego komponentu, trudno jest zrozumieć, dlaczego tutaj zagnieżdżasz obiekty w swoim stanie. Nie jest dobrym pomysłem zagnieżdżanie obiektów w stanie komponentu. Spróbuj ustawić swój stan początkowy na:

this.state = {
  name: 'jasper',
  age: 28
}

W ten sposób, jeśli chcesz zaktualizować nazwę, możesz po prostu zadzwonić:

this.setState({
  name: 'Sean'
});

Czy osiągniesz to, do czego dążysz?

W przypadku większych, bardziej złożonych magazynów danych użyłbym czegoś takiego jak Redux. Ale to znacznie bardziej zaawansowane.

Ogólna reguła dotycząca stanu komponentu polega na używaniu go tylko do zarządzania stanem interfejsu użytkownika komponentu (np. Aktywny, timery itp.)

Sprawdź te referencje:

Mcccambridge
źródło
1
Użyłem tego tylko jako przykładu, obiekt musi być zagnieżdżony. Prawdopodobnie powinienem używać Redux, ale staram się zrozumieć React fundementals ..
JohnSnow
2
Tak, na razie trzymałbym się z dala od Redux. Podczas nauki podstaw staraj się, aby Twoje dane były proste. Pomoże Ci to uniknąć dziwnych przypadków, które Cię potkną. 👍
mccambridge
2

Inna opcja: zdefiniuj zmienną z obiektu Jasper, a następnie po prostu wywołaj zmienną.

Operator rozrzutu: ES6

this.state = {  jasper: { name: 'jasper', age: 28 } } 

let foo = "something that needs to be saved into state" 

this.setState(prevState => ({
    jasper: {
        ...jasper.entity,
        foo
    }
})
Luis Martins
źródło
2

Prosty i dynamiczny sposób.

Wykona to zadanie, ale musisz ustawić wszystkie identyfikatory nadrzędne, aby rodzic wskazywał nazwę obiektu, id = "jasper" i nazwał nazwę elementu wejściowego = właściwość wewnątrz obiektu jasper .

handleChangeObj = ({target: { id , name , value}}) => this.setState({ [id]: { ...this.state[id] , [name]: value } });
Alejandro Sanchez Duran
źródło
Zgadzam się z pierwszą linią, ponieważ jest to jedyna rzecz, która zadziałała dla mojego obiektu obiektów w stanie! Wyczyściłem również, gdzie utknąłem w aktualizacji moich danych. Zdecydowanie najprostszy i dynamiczny sposób ... Dzięki !!
Smit,
2

Możesz spróbować z tym : ( Uwaga : nazwa znacznika wejściowego === pole obiektu)

<input name="myField" type="text" 
      value={this.state.myObject.myField} 
     onChange={this.handleChangeInpForm}>
</input>

-----------------------------------------------------------
handleChangeInpForm = (e) => {
   let newObject = this.state.myObject;
   newObject[e.target.name] = e.target.value;
   this.setState({
     myObject: newObject 
   })
}
Xuanduong Ngo
źródło
Witamy w Stack Overflow. Chociaż ten kod może odpowiedzieć na pytanie, zapewnienie dodatkowego kontekstu dotyczącego tego, dlaczego i / lub jak ten kod odpowiada na pytanie, poprawia jego długoterminową wartość. Jak odpowiedzieć
Elletlar
2

to jest inne rozwiązanie używając immer narzędzia immutabe, bardzo nadaje się do głęboko zagnieżdżonych obiektów z łatwością, a ty nie powinien dbać o mutacji

this.setState(
    produce(draft => {
       draft.jasper.name = 'someothername
    })
)
samehanwar
źródło
1

Ponadto, postępując zgodnie z rozwiązaniem Alberto Pirasa, jeśli nie chcesz skopiować całego obiektu „stanowego”:

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

    let jasperCopy = Object.assign({}, this.state.jasper);
    jasperCopy[inputName].name = inputValue;

    this.setState({jasper: jasperCopy});
  }
Marc LaQuay
źródło
1

Możesz spróbować z tym:

this.setState(prevState => {
   prevState = JSON.parse(JSON.stringify(this.state.jasper));
   prevState.name = 'someOtherName';
   return {jasper: prevState}
})

lub w przypadku innej nieruchomości:

this.setState(prevState => {
   prevState = JSON.parse(JSON.stringify(this.state.jasper));
   prevState.age = 'someOtherAge';
   return {jasper: prevState}
})

Lub możesz użyć funkcji handleChage:

handleChage(event) {
   const {name, value} = event.target;
    this.setState(prevState => {
       prevState = JSON.parse(JSON.stringify(this.state.jasper));
       prevState[name] = value;
       return {jasper: prevState}
    })
}

i kod HTML:

<input 
   type={"text"} 
   name={"name"} 
   value={this.state.jasper.name} 
   onChange={this.handleChange}
/>
<br/>
<input 
   type={"text"} 
   name={"age"} 
   value={this.state.jasper.age} 
   onChange={this.handleChange}
/>
Nemanja Stojanovic
źródło
Działa to bez JSON.stringify .. this.setState (prevState => {prevState = this.state.document; prevState.ValidDate = event.target.value; prevState.ValidDateFormat = dayFormat; return {document: prevState}}); ..Gdzie dokument jest obiektem typu państwowego ..
Oleg Borodko
1

Bez użycia Async i Oczekuj Użyj tego ...

funCall(){    
     this.setState({...this.state.jasper, name: 'someothername'});
}

Jeśli korzystasz z Async i Oczekuj, użyj tego ...

async funCall(){
      await this.setState({...this.state.jasper, name: 'someothername'});
}
Shantanu Sharma
źródło
0

Ta konfiguracja działała dla mnie:

let newState = this.state.jasper;
newState.name = 'someOtherName';

this.setState({newState: newState});

console.log(this.state.jasper.name); //someOtherName
LaZza
źródło