Оновлення об'єкта за допомогою 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.