Реагируйте: как обновить state.item [1] в состоянии используя setState?


260

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

Компонент доступен как 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()

Дополнение после редактирования:

Ознакомьтесь с уроком « Простое взаимодействие с компонентами» из программы response-training, чтобы узнать, как передать функцию обратного вызова от родительского компонента, поддерживающего состояние, к дочернему компоненту, который должен инициировать изменение состояния.


126

Поскольку в этой теме много дезинформации, вот как вы можете сделать это без вспомогательных библиотек:

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

5
@TranslucentCloud Ах да, методы помощника определенно хороши, но я думаю, что каждый должен знать, что происходит под капотом :-)
mpen

Зачем нам нужны шаги 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 . Лучше всего изменить это, чтобы это не смутило будущих читателей.
Джеймс З.

8
Просто хотел отметить, что многие комментарии не имеют значения сейчас из-за правок, и правильный путь - это действительно правильный путь.
Боже,

50

Чтобы изменить глубоко вложенные объекты / переменные в состоянии React, обычно используются три метода: vanilla JavaScript's Object.assign, immutability-helper и cloneDeepиз Lodash .

Для этого есть множество других менее популярных сторонних библиотек, но в этом ответе я расскажу только об этих трех вариантах. Кроме того, существуют некоторые дополнительные методы JavaScript, такие как распространение массива (см., Например, ответ @ mpen), но они не очень интуитивны, просты в использовании и способны обрабатывать все ситуации с манипуляциями с состоянием.

Как отмечалось, бесчисленное количество раз в топ-голосующих комментариях к ответам, авторы которых предлагают прямую мутацию состояния: просто не делайте этого . Это вездесущий анти-паттерн React, который неизбежно приведет к нежелательным последствиям. Учитесь правильно.

Давайте сравним три широко используемых метода.

Учитывая это состояние объекта структуры:

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

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

1. Ванильный JavaScript Object.assign

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()setter для замены оригинала outerв состоянии вновь созданным newOuterобъектом (изменится только значение, имя свойства outerне будет).

Теперь представьте, что у нас есть более глубокое состояние, как state = { outer: { inner: { innerMost: 'initial value' } } }. Мы могли бы попытаться создать newOuterобъект и заполнить его outerсодержимым из состояния, но Object.assignне сможем скопировать innerMostзначение в этот вновь созданный newOuterобъект, поскольку innerMostоно вложено слишком глубоко.

Вы все еще можете копировать inner, как в примере выше, но, поскольку теперь это объект, а не примитив, ссылка from 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>

CloneDeep от Lodash более прост в использовании. Он выполняет глубокое клонирование , поэтому это надежный вариант, если у вас достаточно сложное состояние с многоуровневыми объектами или массивами внутри. Просто 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.

если ты манипулируете огромным и / или сложным состоянием , Lodash's cloneDeep- мудрый выбор.

Если тебе надо расширенные возможности , то есть, если ваша структура состояний сложна и вам нужно выполнять с ней все виды операций, попробуйте immutability-helper, это очень продвинутый инструмент, который можно использовать для манипулирования состояниями.

... или ты действительно должны сделать это вообще?

Если вы храните сложные данные в состоянии React, возможно, сейчас самое время подумать о других способах их обработки. Задание объектов сложных состояний прямо в компонентах React не является простой операцией, и я настоятельно рекомендую подумать о разных подходах.

Скорее всего, вам лучше не хранить свои сложные данные в хранилище Redux, устанавливать их там с помощью редукторов и / или саг и обращаться к ним с помощью селекторов.


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

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

То, что вы определили на самом деле, shallow copyа не deep copy. Легко спутать, что shallow copyзначит. В shallow copy, a !== bно для каждого ключа из исходного объекта a,a[key] === b[key]
Awol

Дааа, прямо упоминается поверхностность Object.assignв ответе.
Нейротрансмиттер

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

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. Однако из-за объяснения это выделяется.
Сураб

Да, этот код работает отлично и очень просто ..
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нигде не определено.
Эдвард Д'Суза

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 ржавый, и я учусь реагировать на мой домашний проект, поэтому я понятия не имею, хорошо это или плохо :-)
Greg0ry

4

Это действительно просто.

Сначала вытащите весь объект items из состояния, обновите часть объекта items по своему усмотрению и верните весь объект items обратно в состояние через 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метода from 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-names

из подлежащего обновлению члена массива - это более вложенный сложный объект, использующий соответствующий метод глубокого копирования основанный на сложности.

Конечно, есть лучшие способы обработки параметров макета, но речь идет о том, как обрабатывать массивы. Соответствующие значения для каждого дочернего элемента также могут быть вычислены вне их, но я нашел более удобным передавать параметр containerState вниз, чтобы они могли выбирать свойства по своему желанию и обновлять массив родительских состояний по их заданному индексу.

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

Используйте событие on, handleChangeчтобы выяснить элемент, который изменился, а затем обновите его. Для этого вам может потребоваться изменить какое-либо свойство, чтобы идентифицировать его и обновить.

Смотрите скрипку https://jsfiddle.net/69z2wepo/6164/


0

Я бы переместить изменение дескриптора функции и добавить параметр индекса

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

в компонент динамической формы и передать его компоненту 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"/>

Никогда не изменяйте состояние React напрямую.
Нейротрансмиттер

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? Если вы это сделаете, разве это не оставит штат только с одним предметом впоследствии?
micnil

Это введет новое свойство 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 this.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',
});

Никогда не изменяйте состояние React напрямую.
Нейротрансмиттер

-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

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