Оновлення об'єкта за допомогою setState в React


265

Чи взагалі можливо оновити властивості об’єкта за допомогою setState?

Щось на зразок:

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

Я намагався:

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

і це:

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

Перший призводить до помилки синтаксису, а другий просто нічого не робить. Будь-які ідеї?


5
другий код спрацював би, проте ви втратили б ageмайно всередині jasper.
giorgim

Відповіді:


577

Є кілька способів зробити це, так як оновлення стану є асинхронної операцією , тому для поновлення стану об'єкта, ми повинні використовувати оновлення функції з setState.

1- Найпростіший:

Спочатку створіть копію, jasperа потім зробіть зміни в цьому:

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

Замість використання Object.assignми можемо також написати це так:

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

2- Використання оператора спред :

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

Примітка: Object.assign і Spread Operatorстворюється лише неглибока копія , тому якщо ви визначили вкладений об'єкт або масив об'єктів, вам потрібен інший підхід.


Оновлення вкладеного об'єкта стану:

Припустимо, ви визначили стан як:

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

Щоб оновити додатковий сир об'єкта піци:

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

Оновлення масиву об’єктів:

Припустимо, у вас є додаток todo, і ви керуєте даними у цій формі:

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

Щоб оновити стан будь-якого об'єкта todo, запустіть карту на масиві і перевірте наявність унікального значення кожного об'єкта, у випадку condition=trueповернення нового об'єкта з оновленим значенням, інакше того ж об'єкта.

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

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

}))

Пропозиція: Якщо об'єкт не має унікального значення, використовуйте індекс масиву.


Те, як я намагався, зараз працює, хоча ... другий спосіб: S
JohnSnow

7
@JohnSnow у вас вдруге видалить інші властивості з jasperоб’єкта, console.log(jasper)хіба ви побачите лише одну клавішу name, віку там не буде :)
Mayank Shukla

1
@JohnSnow, так цей спосіб є правильним, якщо jasperє object, чи не найкраще чи ні, можливо, можливі й інші кращі рішення :)
Mayank Shukla

6
Тут добре зазначити, що Object.assignані розповсюджені властивості копіювання в глибині оператора ні. Таким чином, ви повинні використовувати обхідні шляхи, такі як лодаш deepCopyтощо.
Олексій Василев

1
Як бій let { jasper } = this.state?
Брайан Ле

37

Це найшвидший і найчитабельніший спосіб:

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

Навіть якщо this.state.jasperвже міститься властивість імені, використовується нове ім'я name: 'someothername'.


38
У цього рішення є один великий недолік: для оптимізації оновлень стану React може групувати кілька оновлень. Як наслідок this.state.jasper, не обов'язково містити останній стан. Краще використовувати позначення разом із prevState.
згрішив

33

Тут використовуйте оператор розповсюдження та деякі ES6

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

Це оновлення яшми, але інші властивості видаляються зі стану.
iaq

11

Я використав це рішення.

Якщо у вас вкладено такий стан:

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

ви можете оголосити функцію handleChange, яка копіює поточний стан і повторно присвоює йому змінені значення

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

тут html зі слухачем події. Обов’язково використовуйте те саме ім’я, яке використовується в об'єкті стану (у цьому випадку 'friendName')

<input type="text" onChange={this.handleChange} " name="friendName" />

Це працювало для мене, за винятком того, що я повинен був використовувати це замість цього:statusCopy.formInputs[inputName] = inputValue;
MikeyE

9

Я знаю, що тут є багато відповідей, але я здивований, що жодна з них не створює копію нового об’єкта поза setState, а потім просто встановлює setState ({newObject}). Чистий, лаконічний і надійний. Отже, у цьому випадку:

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

Або для динамічної властивості (дуже корисно для форм)

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


7

спробуйте це, це повинно працювати добре

this.setState(Object.assign(this.state.jasper,{name:'someOtherName'}));

4
Ви мутуєте об'єкт стану безпосередньо. Щоб використовувати цей підхід, додайте новий об’єкт як джерело:Object.assign({}, this.state.jasper, {name:'someOtherName'})
Albizia

3

Перший випадок - це дійсно синтаксична помилка.

Оскільки я не бачу решти вашого компонента, важко зрозуміти, чому ви тут вкладаєте об’єкти у своєму штаті. Це не дуже гарна ідея вкладати об’єкти в компонентний стан. Спробуйте встановити початковий стан таким:

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

Таким чином, якщо ви хочете оновити ім'я, ви можете просто зателефонувати:

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

Чи вдасться досягти того, чого ти прагнеш?

Для великих, складніших сховищ даних я б використовував щось на зразок Redux. Але це набагато досконаліше.

Загальне правило зі станом компонента - використовувати його лише для управління станом UI компонента (наприклад, активний, таймери тощо)

Перевірте ці посилання:


1
Я використовував це лише як приклад, об'єкт повинен бути вкладений .. Я, мабуть, повинен використовувати Redux, але я намагаюся зрозуміти React fundementals ..
JohnSnow

2
Так, я б поки що тримався подалі від Redux. Поки ви вивчаєте основи, постарайтеся, щоб ваші дані були простими. Це допоможе вам уникнути незвичайних випадків, які вас обтяжують. 👍
mccambridge

2

Інший варіант: визначте свою змінну з об’єкта Jasper, а потім просто зателефонуйте до змінної.

Оператор розповсюдження: 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
    }
})

2

Простий і динамічний спосіб.

Це зробить роботу, але вам потрібно встановити всі ідентифікатори для батьків, щоб батько вказував на ім'я об'єкта, будучи id = "jasper" та іменем імені вхідного елемента = властивості всередині об'єкта jasper .

handleChangeObj = ({target: { id , name , value}}) => this.setState({ [id]: { ...this.state[id] , [name]: value } });

Я погоджуюся з першим рядком, оскільки це єдине, що працювало на мій об’єкт в державі! Також було зрозуміло, де я застряг в оновленні своїх даних. На сьогоднішній день самий простий і динамічний спосіб ... Дякую !!
Smit

2

Ви можете спробувати з цим : ( Примітка : назва вхідного тегу === поле об'єкта)

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

Ласкаво просимо до переповнення стека. Хоча цей код може відповісти на питання, надаючи додатковий контекст щодо того, чому та / або як цей код відповідає на питання, покращує його довгострокове значення. Як відповісти
Elletlar

2

це ще одне рішення з використанням утиліти immer , що дуже добре підходить для глибоко вкладених об'єктів, і вам не слід піклуватися про мутацію

this.setState(
    produce(draft => {
       draft.jasper.name = 'someothername
    })
)

1

Крім того, дотримуючись рішення Альберто Піраса, якщо ви не хочете скопіювати весь об'єкт "держави":

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

1

Ви можете спробувати з цим:

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

або для іншого майна:

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

Або ви можете використовувати функцію 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}
    })
}

та 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}
/>

Це працює без JSON.stringify .. this.setState (prevState => {prevState = this.state.document; prevState.ValidDate = event.target.value; prevState.ValidDateFormat = dayFormat; повернути {документ: prevState}}); .. Де документ є об'єктом державного типу ..
Олег Бородко

1

Не використовуючи Async і чекайте, скористайтеся цим ...

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

Якщо ви використовуєте програму Async And Await, скористайтеся цим ...

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

0

Ця установка працювала для мене:

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

this.setState({newState: newState});

console.log(this.state.jasper.name); //someOtherName
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.