Реагуйте: як оновити state.item [1] у стані за допомогою setState?


258

Я створюю додаток, де користувач може створити власну форму. Наприклад, вкажіть назву поля та детальну інформацію про інші стовпці, які слід включити.

Компонент доступний як JSFiddle тут .

Мій початковий стан виглядає приблизно так:

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

Я хочу оновити стан, коли користувач змінює будь-яке значення, але мені важко орієнтуватися на правильний об’єкт:

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

Як я повинен виготовити, this.setStateщоб оновити його items[1].name?



1
яка ваша обрана відповідь на це питання?
Браян Меллор

Відповіді:


126

Ви можете використовувати updateпомічник непорушності для цього :

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

Або якщо вам не байдуже, чи зможете виявити зміни цього елемента shouldComponentUpdate()методом життєвого циклу ===, ви можете безпосередньо відредагувати стан і змусити компонент повторно відображати - це фактично те саме, що відповідь @limelights, оскільки це витягнення об'єкта зі стану та його редагування.

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

Додавання після редагування:

Ознайомтесь з уроком простий компонентної комунікації з тренінгу реагування на прикладі того, як передавати функцію зворотного дзвінка від батька, що тримає стан, до дочірнього компонента, який повинен викликати зміну стану.


125

Оскільки в цій темі є багато дезінформації, ось як це можна зробити, не маючи помічників:

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

Ви можете комбінувати кроки 2 і 3, якщо хочете:

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

Або ви можете зробити все це в один рядок:

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

Примітка. Я створив itemsмасив. ОП використовував об’єкт. Однак поняття однакові.


Ви можете побачити, що відбувається у вашому терміналі / консолі:

 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
О так, допоміжні методи, безумовно, приємні, але, думаю, всі повинні знати, що відбувається під кришкою :-)
'18

Для чого нам потрібні кроки 2 і 3? Чому ми не можемо мутувати клон після першого кроку безпосередньо?
Євморов

1
@Evmorov Тому що це не глибокий клон. Крок 1 - це просто клонування масиву, а не об'єкти всередині. Іншими словами, кожен із об'єктів всередині нового масиву все ще "вказує" на існуючі об'єкти в пам'яті - мутуючи один, буде мутувати другий (вони є одним і тим же). Дивіться також items[0] === clone[0]біт у моєму прикладі терміналу внизу. Потрійна =перевірка, чи об'єкти посилаються на одне і те ж.
mpen

Складніше стає, якщо ви не знаєте індексу елемента в масиві.
Ян Варбуртон

@IanWarburton Не дуже. items.findIndex()повинні зробити коротку роботу над цим.
mpen

92

Неправильний шлях!

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

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

Як наголошують у коментарях набагато кращі розробники: мутувати стан - це неправильно!

Мені потрібен час, щоб зрозуміти це. Вище працює, але це забирає силу Реагувати. Наприклад, componentDidUpdateце не буде розглядатися як оновлення, оскільки воно змінено безпосередньо.

Тож правильним шляхом було б:

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

25
ES6 тільки тому, що ви використовуєте "const"?
nkkollaw

49
Не викликає items[1].role = e.target.valueстан, що мутує, безпосередньо?
Антоній

28
ви мутуєте стан, це абсолютно проти ідеї збереження стану непорушною, як запропонована реакція. Це може доставити вам біль при великому застосуванні.
ncubica

8
@MarvinVK, у вашій відповіді сказано: "Так найкращою практикою було б:" з подальшим використанням "this.forceUpdate ();" що не рекомендується, оскільки це може бути перезаписано setState (), див. facebook.github.io/react/docs/react-component.html#state . Найкраще це змінити, щоб це не бентежило майбутніх читачів.
Джеймс З.

7
Просто хотілося зазначити, що багато коментарів зараз не мають значення через зміни, і правильний шлях насправді є правильним.
heez

50

Для того, щоб змінити глибоко вкладені об'єкти / змінні в реактив стану, як правило , використовується три методи: ваніль в JavaScript Object.assign, незмінність-помічник і cloneDeepвід Lodash .

Існує також безліч інших менш популярних сторонніх прихильників для досягнення цього, але в цій відповіді я висвітлю саме ці три варіанти. Також існують деякі додаткові методи ванільного JavaScript, як-от розсип масиву (див. Наприклад відповідь @ mpen), але вони не дуже інтуїтивно зрозумілі, прості у використанні та здатні впоратися з усіма ситуаціями маніпулювання державою.

Як неодноразово вказувалося, вгорі голосували коментарі до відповідей, автори яких пропонують пряму мутацію стану: просто не робіть цього . Це повсюдна антиреакція React, яка неминуче призведе до небажаних наслідків. Дізнайтеся правильний шлях.

Порівняємо три широко використовувані методи.

З огляду на таку структуру об'єкта стану:

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

Ви можете використовувати наступні методи для оновлення значення самого внутрішнього innerполя, не впливаючи на решту стану.

1. Object.assign ванільного JavaScript

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>

Майте на увазі, що Object.assign не буде виконувати глибоке клонування , оскільки він копіює лише значення властивостей , і тому те, що він робить, називається дрібною копією (див. Коментарі).

Щоб це працювало, нам слід лише маніпулювати властивостями примітивних типів ( outer.inner), тобто рядків, чисел, булів.

У цьому прикладі ми створюємо нову константу ( const newOuter...), використовуючи Object.assign, яка створює порожній об’єкт ( {}), копіює в нього outerоб'єкт ( { inner: 'initial value' }), а потім копіює інший об’єкт { inner: 'updated value' } над ним.

Таким чином, врешті-решт, новостворена newOuterконстанта утримуватиме значення з { inner: 'updated value' }моменту innerпереоцінки властивості. Це newOuterабсолютно новий об'єкт, який не пов'язаний з об'єктом у стані, тому він може бути вимкнено, якщо потрібно, і стан залишиться таким самим і не зміниться, поки не буде запущена команда оновлення.

Остання частина полягає у використанні setOuter()сеттера для заміни оригіналу outerв стані новоствореним newOuterоб'єктом (зміниться лише значення, ім'я властивості outerне буде).

А тепер уявімо, що у нас є більш глибокий стан state = { outer: { inner: { innerMost: 'initial value' } } }. Ми можемо спробувати створити newOuterоб’єкт і заповнити його outerвмістом у державі, але Object.assignне зможемо скопіювати innerMostзначення цього новоствореного newOuterоб’єкта, оскільки innerMostвін вкладений занадто глибоко.

Ви все ще можете скопіювати inner, як у наведеному вище прикладі, але оскільки це тепер об'єкт, а не примітив, посилання від newOuter.innerбуде скопійовано outer.innerнатомість, це означає, що ми в кінцевому підсумку з локальним newOuterоб’єктом, безпосередньо прив'язаним до об'єкта в державі .

Це означає, що в цьому випадку мутації локально створених newOuter.innerбезпосередньо впливатимуть на outer.innerоб'єкт (у стані), оскільки вони насправді стали тим самим (в пам'яті комп'ютера).

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

Якщо у вас є більш глибокі об'єкти (2-й рівень або більше), які слід оновити, не використовуйте Object.assign. Ви ризикуєте мутувати стан безпосередньо.

2. Клон ЛодашаГлубокий

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>

ClonasDeep клонада Лодаша - простіший у використанні. Він виконує глибоке клонування , тому він є надійним варіантом, якщо у вас досить складний стан з багаторівневими об'єктами або масивами всередині. Просто cloneDeep()властивість державного рівня вищого рівня, мутуйте клоновану частину будь-яким способом, і setOuter()поверніть її до держави.

3. незмінність-помічник

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-helperбере його на зовсім новий рівень, і прохолодна річ про це є те , що він може не тільки $setзначення для державних елементів, але і $push, $splice, $merge( і т.д.) їх. Ось список доступних команд .

Бічні нотатки

Знову ж таки, майте на увазі, що setOuterзмінює лише властивості першого рівня об'єкта стану ( outerу цих прикладах), а не глибоко вкладені ( outer.inner). Якби він поводився по-іншому, цього питання не існувало б.

Який з них підходить для вашого проекту?

Якщо ви не хочете або не можете використовувати зовнішні залежності та маєте просту структуру стану , дотримуйтесь Object.assign.

Якщо ви маніпулюєте величезним та / або складним станом , Лодаш cloneDeep- це мудрий вибір.

Якщо вам потрібні розширені можливості , тобто, якщо ваша державна структура є складною і вам потрібно виконувати всі види операцій над нею, спробуйте immutability-helper, це дуже просунутий інструмент, який можна використовувати для маніпулювання станом.

... або, чи справді взагалі потрібно це робити?

Якщо ви тримаєте складні дані в стані React, можливо, це вдалий час, щоб подумати про інші способи поводження з ними. Встановлення складних об'єктів стану прямо в компонентах React не є простою операцією, і я настійно пропоную подумати про різні підходи.

Швидше за все, вам краще не зберігати складні дані в магазині Redux, встановлювати їх там, використовуючи редуктори та / або саги, і отримувати доступ до них за допомогою селекторів.


Object.assignне виконує глибоку копію. Скажіть, якщо a = {c: {d: 1}}і b = Object.assign({}, a), тоді ви виконуєте b.c.d = 4, тоді a.c.dвін мутується.
Авол

Ви маєте рацію, значення 1найпотаємнішого об'єкта ( a.c.d) буде вимкнено. Але якщо ви перепризначите наступника першого рівня b, як це:, b.c = {f: 1}відповідна частина aне буде вимкнена (вона залишиться {d: 1}). Приємний вилов все одно, я одразу оновлю відповідь.
Нейромедіатор

Те, що ви визначили, насправді є a, shallow copyа не a deep copy. Легко переплутати, що shallow copyозначає. В shallow copy, a !== bале для кожного ключа від вихідного об'єкта a,a[key] === b[key]
Авол

Так, у відповіді прямо вказана мілкості Object.assign.
Нейромедіатор

JSON.parse(JSON.stringify(object))також є варіантом для глибокого клонування. Продуктивність гірша, ніж квартир, cloneDeepхоча. measurethat.net/Benchmarks/Show/2751/0 / ...
Тилік

35

У мене була така ж проблема. Ось просте рішення, яке працює!

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

11
@TranslucentCloud - це, звичайно, не пряма мутація. вихідний масив клонували, модифікували, а потім стан встановлювали знову за допомогою клонованого масиву.
vsync

@vsync Так, тепер після редагування оригінальної відповіді це взагалі не є мутацією.
Нейромедіатор

2
@TranslucentCloud - Раніше також моя редакція не мала нічого спільного з цим, дякую за ставлення. @ Jonas тут зробив лише просту помилку у своїй відповіді, використовуючи {фігурні дужки замість дужок, які я виправив
vsync

31

Згідно з документацією React на setState , використання, Object.assignяк пропонують інші відповіді, тут не є ідеальним. Через характер setStateасинхронної поведінки, наступні дзвінки за допомогою цієї методики можуть змінити попередні дзвінки, викликаючи небажані результати.

Натомість документи React рекомендують використовувати форму оновлення, setStateяка працює в попередньому стані. Майте на увазі, що при оновленні масиву або об'єкта ви повинні повернути новий масив або об’єкт, оскільки React вимагає від нас збереження незмінності стану. Використання оператора розширення синтаксису ES6 для дрібної копіювання масиву, створення або оновлення властивості об'єкта в заданому індексі масиву виглядатиме так:

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

2
Це відповідна відповідь, якщо ви використовуєте ES6. Це те, що відповів @Jonas. Однак завдяки поясненню це виділяється.
Sourabh

Так, цей код працює чудово і дуже просто.
Shoeb Mirza

29

Спочатку дістаньте потрібний предмет, змініть те, що ви хочете на цьому об'єкті, і поверніть його на стан. Те, як ви використовуєте стан, лише передаючи об'єкт, getInitialStateбуло б простіше, якби ви використовували об'єкт з ключем.

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

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

3
Ні, це поступається Uncaught TypeError: Cannot read property 'items' of null.
мартіни

Ні, це не буде. Ваша помилка, швидше за все, випливає з того, як ви робите getInitialState.
Генрік Андерссон

10
@HenrikAndersson У вашому прикладі щось не здається правильним. itemsніде не визначено.
Edward D'Souza

1
@ EdwardD'Souza Ви абсолютно праві! Моя відповідь - показати, як це слід визначити та використовувати. Те, як налаштований код запитувача, не працює для того, що він / вона хотів би, тому потрібна потреба в об'єкті, що вводить ключ.
Генрік Андерссон

7
Це типовий антидіапазон React, ви призначаєте itemпосилання на власну this.state.items[1]змінну стану. Потім ви модифікуєте item( item.name = 'newName'), і таким чином мутуєте стан безпосередньо, що сильно не рекомендується. У вашому прикладі навіть дзвонити не потрібно this.setState({items: items}), оскільки стан вже мутовано.
Нейромедіатор

22

Не мутуйте стан на місці. Це може спричинити несподівані результати. Я навчився свого уроку! Завжди працювати з копією / клоном, Object.assign()це добре:

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
що itemsу вашому прикладі? Ви мали на увазі this.state.itemsчи щось інше?
Бух Бух

Я перевірив це, але йому не вистачає одного рядка. Зверху items[1] = item;має бути рядковий вислів items = this.state.items;. Остерігайся, що мій JavaScript іржавий, і я
вчусь

4

Це дійсно просто.

Спочатку витягніть весь об'єкт із стану, оновіть частину об'єктів за бажанням та поверніть весь об'єкт у стан через 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 буде працювати, якщо у вас є відносно проста однорівнева глибока структура стану з найглибшими членами, що містять значення примітивного типу".
Нейромедіатор

3

Оскільки жоден із перерахованих вище варіантів не був ідеальним для мене, я в кінцевому підсумку використовував карту:

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

2

Мутація безкоштовна:

// 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
Я не бачу, де newItemsвстановлено.
Нейромедіатор

Чи не карта плюс порівняння в масиві жахливі для продуктивності?
Натасія Таварес

@NatassiaTavares .. що? можливо, вас плутають з fo ofабоforEach карта найшвидша.
Діно

1

Виявив це напрочуд важко, і жодна магія поширення ES6 не здавалася спрацьованою, як очікувалося. Використовувала таку структуру, щоб отримати властивості відображених елементів для цілей компонування.

знайдений з використанням updateметоду від immutability-helperяк найбільш прямого в цьому спрощеному прикладі:

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

як адаптовано з https://github.com/kolodny/immutability-helper#computed-property-name

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

Звичайно, є кращі способи обробки параметрів компонування, але це стосується того, як обробляти масиви. Відповідні значення для кожного дочірнього елемента можна також обчислити поза ними, але я вважаю, що зручніше передавати контейнерState вниз, щоб вони могли отримувати властивості за бажанням та оновлювати масив батьківського стану за вказаним індексом.

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

Використовуйте карту масиву з функцією стрілки в одному рядку

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

1
Це в основному ідентично цій іншій відповіді, опублікованій близько року тому
CertainPerformance

0

Використовуйте подію на, handleChangeщоб з’ясувати елемент, який змінився, а потім оновіть його. Для цього вам може знадобитися змінити якесь властивість, щоб визначити його та оновити.

Дивіться скрипку https://jsfiddle.net/69z2wepo/6164/


0

Я б перемістив зміни функції ручки та додав параметр індексу

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

до компонента форми Dynamic і передайте його компоненту PopulateAtCheckboxes як опору. Під час переходу на елементи ви можете включити додатковий лічильник (який називається індексом у коді нижче), який слід передавати зміні ручки, як показано нижче

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

Нарешті, ви можете зателефонувати слухачеві змін, як показано нижче

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

Ніколи не мутуйте стан Реакта безпосередньо.
Нейромедіатор

0

Якщо вам потрібно змінити лише частину Array, компонент реагує із встановленим станом.

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

Найкраще , щоб оновити red-oneв Arrayтакий спосіб :

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)

що таке newArray? ти маєш на увазі newItems? Якщо ви зробите це, чи не залишив би цей стан після цього лише один предмет?
мікніл

Це дозволить ввести нову властивість newItemsдо stateоб'єкту, а не оновлювати існуючу itemsнерухомість.
нейромедіатор

0

або якщо у вас є динамічно генерований список і ви не знаєте індексу, а просто маєте ключ або ідентифікатор:

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

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

    ItemsCopy.push(entry)
})


this.setState({Items:ItemsCopy});

0

Спробуйте з кодом:

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

this.setState({items: cloneObj });

0

Наступний фрагмент коду легко пішов на мій тупий мозок. Видалення об’єкта та заміна оновленим

    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

Як щодо створення іншого компонента (для об'єкта, якому потрібно зайти в масив) і передати наступний як реквізит?

  1. індекс компонента - індекс буде використовуватися для створення / оновлення масиву.
  2. функція set - Ця функція передає дані в масив на основі індексу компонента.
<SubObjectForm setData={this.setSubObjectData}                                                            objectIndex={index}/>

Тут {index} можна передати на основі позиції, де використовується ця SubObjectForm.

і setSubObjectData може бути щось подібне.

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

У SubObjectForm цей.props.setData може бути викликаний при зміні даних, як зазначено нижче.

<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
це не є оптимальним, ви повторите весь масив, щоб оновити лише другий елемент?
wscourge

Ви також мутуєте елемент у першому масиві, також вам слід скористатисяitem = Object.assign({}, item, {name: 'newName'});
remram

0

@ Відповідь JonnyBuchanan працює відмінно, але лише для змінної стану масиву. Якщо змінна стану є лише одним словником, дотримуйтесь цього:

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

Ви можете замінити [input]назвою поля вашого словника та e.target.valueйого значенням. Цей код виконує завдання оновлення для події зміни входу моєї форми.


-3

Спробуйте це, безумовно, спрацює, в іншому випадку я намагався, але не працював

import _ from 'lodash';

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

Ніколи не мутуйте стан Реакта безпосередньо.
Нейромедіатор

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

та Змінити з поля "Текст" додати подію "Змінити"

onChange = {
   (event) => {                                                
      this.handleChanges(event.target.value, "title");
   }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.