Как обновить свойства вложенного состояния в React

390

Я пытаюсь организовать свое состояние с помощью вложенного свойства следующим образом:

this.state = {
   someProperty: {
      flag:true
   }
}

Но обновление состояния, как это,

this.setState({ someProperty.flag: false });

не работает Как это можно сделать правильно?

Алекс Йонг
источник
4
Что значит не работает? Это не очень хороший вопрос - что случилось? Есть ошибки? Какие ошибки?
Даррен Суини
Попробуйте прочитать: stackoverflow.com/questions/18933985/…
Андрей Парамошкин
16
Ответ: не используйте вложенное состояние в React . Прочитайте этот отличный ответ.
Qwerty
4
Что следует использовать вместо этого?
Янис Элмерис
42
Просто не использовать вложенное состояние является неприемлемым ответом на то, насколько широко используется React сегодня. Эта ситуация возникнет, и разработчикам нужен ответ на это.
serraosays

Ответы:

456

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

var someProperty = {...this.state.someProperty}
someProperty.flag = true;
this.setState({someProperty})

Идея состоит в том, чтобы создать фиктивный объект, выполнить над ним операции, а затем заменить состояние компонента на обновленный объект.

Теперь оператор распространения создает только одну вложенную копию объекта уровня. Если ваше состояние сильно вложено, как:

this.state = {
   someProperty: {
      someOtherProperty: {
          anotherProperty: {
             flag: true
          }
          ..
      }
      ...
   }
   ...
}

Вы можете установить состояние с использованием оператора распространения на каждом уровне, как

this.setState(prevState => ({
    ...prevState,
    someProperty: {
        ...prevState.someProperty,
        someOtherProperty: {
            ...prevState.someProperty.someOtherProperty, 
            anotherProperty: {
               ...prevState.someProperty.someOtherProperty.anotherProperty,
               flag: false
            }
        }
    }
}))

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

Смотрите этот ответ о том, как обновить состояние с immutability helper.

Шубхам Хатри
источник
2
@ Ohgodwhy, нет, это не имеет прямого доступа к состоянию, так как {... this.state.someProperty} перенастраивает новый объект, somePropertyи мы изменяем это
Шубхам Хатри
4
у меня это не сработало .. я использую реагирующую версию @ 15.6.1
Rajiv
5
@Stophface Мы можем использовать Lodash для глубокого клонирования, но не каждый включит эту библиотеку просто для установки состояния
Shubham Khatri
15
Разве это не нарушает responsejs.org/docs/… ? Если два изменения вложенного объекта были объединены, последнее изменение перезапишет первое. Таким образом, самый правильный способ был бы: this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}}) немного утомительно, хотя ...
olejorgenb
2
Я не думаю, что вам нужно ...prevState,в последнем примере, только вы somePropertyи его дети
Джемар Джонс
140

Написать в одну строку

this.setState({ someProperty: { ...this.state.someProperty, flag: false} });
Yoseph
источник
12
Рекомендуется использовать средство обновления для setState вместо прямого доступа к this.state в setState. actjs.org/docs/react-component.html#setstate
Рагху Теджа
6
Я полагаю, что это говорит, не читайте this.stateсразу после setStateи ожидайте, что это будет иметь новое значение. Против вызова this.stateвнутри setState. Или есть проблема с последним, которая мне не ясна?
trpt4him
2
Как сказал trpt4him , ссылка, которую вы дали, рассказывает о проблеме доступа this.stateпосле setState. Кроме того, мы не переходим this.stateк setState. В раскинутые оператор свойства. Это то же самое, что Object.assign (). github.com/tc39/proposal-object-rest-spread
Йосеф
3
@RaghuTeja, может быть, только создание this.setState(currentState => { someProperty: { ...currentState.someProperty, flag: false} });может позволить избежать этой проблемы?
веб-девушка
Я добавил ответ, как добиться того же с помощью ловушки useState для полноты.
Эндрю
90

Иногда прямые ответы не самые лучшие :)

Укороченная версия:

этот код

this.state = {
    someProperty: {
        flag: true
    }
}

должно быть упрощено как-то вроде

this.state = {
    somePropertyFlag: true
}

Длинная версия:

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

Давайте представим следующее состояние:

{
    parent: {
        child1: 'value 1',
        child2: 'value 2',
        ...
        child100: 'value 100'
    }
}

Что произойдет, если вы измените только значение child1? React не будет повторно отображать представление, поскольку использует поверхностное сравнение и обнаружит, что parentсвойство не изменилось. Кстати, мутирование государственного объекта напрямую считается плохой практикой в ​​целом.

Так что вам нужно заново создать весь parentобъект. Но в этом случае мы столкнемся с другой проблемой. Реакт будет думать, что все дети изменили свои ценности, и сделает все заново. Конечно, это не хорошо для производительности.

Эту проблему еще можно решить, написав некоторую сложную логику, shouldComponentUpdate()но я бы предпочел остановиться здесь и использовать простое решение из короткой версии.

Константин Смолянин
источник
4
Я думаю, что есть случаи, когда вложенное состояние прекрасно. Например, состояние, динамически отслеживающее пары ключ-значение. Смотрите здесь , как вы можете обновить состояние для только одной записи в государстве: reactjs.org/docs/...
Порс
1
Мелкое сравнение используется для чистых компонентов по умолчанию.
Базель Шишани,
4
Я хочу создать алтарь в вашу честь и молиться ему каждое утро. Мне потребовалось три дня, чтобы найти ответ, который прекрасно объясняет ОЧЕРЕДНОЕ дизайнерское решение. Каждый просто пытается использовать оператор распространения или делать другие хаки только потому, что вложенное состояние выглядит более читабельным.
Эдеф
1
Как вы тогда предлагаете использовать React для таких вещей, как рендеринг интерактивного дерева (например, в моей компании есть несколько датчиков в разных частях света, каждый из которых имеет разные типы, с разными свойствами, в разных местах (80+ ) и, конечно, они организованы в иерархию, поскольку каждое развертывание стоит тысячи долларов и является законченным проектом, поэтому было бы невозможно управлять ими без иерархии). Мне очень любопытно, как вы не будете моделировать такие вещи, как деревья, как ... деревья в React.
ДаниАлехандро
10
Кажется, что React не был создан для представления чего-либо, что хранится в виде рекурсивной структуры нетривиальной глубины. Это немного разочаровывает, поскольку древовидные представления появляются практически во всех сложных приложениях (gmail, файловые менеджеры, любые системы управления документами и т. Д.). Я использовал React для этих вещей, но всегда был фанат React, который рассказывал всем, что вы делаете анти-паттерны, и предлагал, чтобы решение содержало глубокие копии дерева в состоянии каждого узла (да, 80 копий). Я хотел бы видеть нехакерский компонент в виде дерева, который не нарушает никаких правил React.
ДаниАлехандро
63

отказ

Вложенное состояние в React - неправильный дизайн

Прочитайте этот отличный ответ .

 

Причины этого ответа:

React setState - это просто встроенное удобство, но вы скоро поймете, что у него есть свои пределы. Использование пользовательских свойств и интеллектуальное использование forceUpdate дает вам гораздо больше. например:

class MyClass extends React.Component {
    myState = someObject
    inputValue = 42
...

Например, MobX полностью скрывает состояние канав и использует настраиваемые наблюдаемые свойства.
Используйте Observables вместо состояния в компонентах React.

 


ответ на ваши страдания - см. пример здесь

Существует еще один более короткий способ обновления любого вложенного свойства.

this.setState(state => {
  state.nested.flag = false
  state.another.deep.prop = true
  return state
})

На одной линии

 this.setState(state => (state.nested.flag = false, state))

примечание: здесь это оператор запятой ~ MDN , посмотрите его в действии (песочница) .

Это похоже на (хотя это не меняет ссылку на состояние)

this.state.nested.flag = false
this.forceUpdate()

Для тонкой разницы в этом контексте между forceUpdateи setStateпосмотрите связанный пример.

Конечно, это злоупотребление некоторыми основными принципами, так как они stateдолжны быть доступны только для чтения, но, поскольку вы немедленно отбрасываете старое состояние и заменяете его новым, это вполне нормально.

Предупреждение

Даже если компонент, содержащий состояние, будет обновляться и перерисовываться должным образом ( кроме этой ошибки ) , реквизиты не смогут распространяться на детей (см. Комментарий Spymaster ниже) . Используйте эту технику, только если вы знаете, что делаете.

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

render(
  //some complex render with your nested state
  <ChildComponent complexNestedProp={this.state.nested} pleaseRerender={Math.random()}/>
)

Теперь, даже если ссылка для complexNestedProp не изменилась ( shouldComponentUpdate )

this.props.complexNestedProp === nextProps.complexNestedProp

компонент будет засавить всякий раз , когда обновления родительского компонента, который является случаем после вызова this.setStateили this.forceUpdateв родительском.

Эффекты мутирования государства

Использование вложенного состояния и мутирует состояние непосредственно опасно , потому что разные объекты могут держать (намеренно или нет) разные (старые) ссылки на состояние и не обязательно знать , когда обновление (при использовании, например , PureComponentили , если shouldComponentUpdateосуществляется для возврата false) или являются предназначен для отображения старых данных, как в примере ниже.

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

состояние потока состояние потока вложенными

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

Qwerty
источник
12
Вы не должны нигде мутировать любое реагирующее состояние. Если есть вложенные компоненты, которые используют данные из state.nested или state.another, они не будут повторно отображаться, как и их дочерние элементы. Это потому, что объект не изменился, поэтому React считает, что ничего не изменилось.
Зерагамба
@ SpyMaster356 Я обновил свой ответ, чтобы решить эту проблему. Также обратите внимание, что, например, MobX полностью рвет stateи использует настраиваемые наблюдаемые свойства. React - setStateэто просто встроенное удобство, но вы скоро поймете, что оно имеет свои пределы. Использование пользовательских свойств и интеллектуальное использование forceUpdateдает вам гораздо больше. Используйте Observables вместо состояния в компонентах React.
Qwerty
Также я добавил пример response-experiment.herokuapp.com/state-flow (ссылка на git вверху)
Qwerty
Что если вы загрузите, например, объект с вложенными атрибутами из базы данных в состояние? Как избежать вложенного состояния в этом случае?
Тобив
3
@tobiv Лучше преобразовать данные при извлечении в другую структуру, но это зависит от варианта использования. Можно иметь вложенный объект или массив объектов в состоянии, если вы думаете о них как о компактных единицах некоторых данных. Проблема возникает, когда вам нужно хранить переключатели пользовательского интерфейса или другие наблюдаемые данные (т.е. данные, которые должны немедленно изменить представление), поскольку они должны быть плоскими, чтобы правильно запускать внутренние алгоритмы React diffing. Для изменений в других вложенных объектах легко вызывать this.forceUpdate()или реализовывать определенные shouldComponentUpdate.
Qwerty
21

Если вы используете ES2015, у вас есть доступ к Object.assign. Вы можете использовать его следующим образом для обновления вложенного объекта.

this.setState({
  someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});

Вы объединяете обновленные свойства с существующими и используете возвращенный объект для обновления состояния.

Редактировать: Добавлен пустой объект в качестве цели в функцию назначения, чтобы убедиться, что состояние не изменено напрямую, как указывал carkod.

Алисса Руз
источник
12
И я могу ошибаться, но я не думаю, что вы используете React без ES2015.
Madbreaks
16
const newState = Object.assign({}, this.state);
newState.property.nestedProperty = "new value";
this.setState(newState);
eyecandy.dev
источник
1
Кажется, здесь более элегантное решение: 1) избегать утомительного распространения внутри setState и 2) красиво ставить новое состояние внутри setState, избегая мутировать непосредственно внутри setState. И последнее, но не менее важное: 3) избегать использования какой-либо библиотеки только для простой задачи
веб-леди
Два примера не эквивалентны. Если propertyобъект содержит несколько вложенных свойств, первое удалит их, второе - нет. codesandbox.io/s/nameless-pine-42thu
Qwerty
Qwerty, вы правы, спасибо за усилия. Я удалил версию ES6.
eyecandy.dev
Просто и понятно!
Тони Сепия
Я использовал ваше решение. Мое состояние obj небольшое, и оно работало нормально. Будет ли React отображаться только для измененного состояния или все будет отображаться?
Кэндзи Ногучи
14

Есть много библиотек, чтобы помочь с этим. Например, используя immutability-helper :

import update from 'immutability-helper';

const newState = update(this.state, {
  someProperty: {flag: {$set: false}},
};
this.setState(newState);

Используя набор lodash / fp :

import {set} from 'lodash/fp';

const newState = set(["someProperty", "flag"], false, this.state);

Использование lodash / fp merge:

import {merge} from 'lodash/fp';

const newState = merge(this.state, {
  someProperty: {flag: false},
});
tokland
источник
Если вы используете lodash, вы, вероятно, захотите попробовать _.cloneDeepустановить состояние клонированной копии.
Исин Лю
@YixingLiu: я обновил ответ для использования lodash/fp, поэтому нам не нужно беспокоиться о мутациях.
Tokland
Любое преимущество выбора mergeили setфункций в lodash / fp помимо синтаксиса?
Тойво Савен
Хороший ответ, может быть, вы хотите добавить, что вам также нужно вызывать this.setState(newState)методы lodash / fp. Другой недостаток заключается в том, что у lodash .set(non-fp) есть другой порядок аргументов, который на некоторое время сбил меня с толку .
Тойво Савен
7

Мы используем Immer https://github.com/mweststrate/immer для решения подобных проблем.

Просто заменили этот код в одном из наших компонентов

this.setState(prevState => ({
   ...prevState,
        preferences: {
            ...prevState.preferences,
            [key]: newValue
        }
}));

С этим

import produce from 'immer';

this.setState(produce(draft => {
    draft.preferences[key] = newValue;
}));

С immer вы обрабатываете свое состояние как «нормальный объект». Магия происходит за сценой с прокси-объектами.

Йоаким Едерберг
источник
6

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

state = {
  someProperty: {
    flag: 'string'
  }
}

handleChange = (value) => {
  const newState = {...this.state.someProperty, flag: value}
  this.setState({ someProperty: newState })
}

Чтобы установить состояние определенного вложенного поля, вы задали весь объект. Я сделал это, создав переменную, newStateи распространение содержимого текущего состояния в него первым использованием ES2015 оператора распространения . Затем я заменил значение this.state.flagна новое (так как я установил flag: value после распространения текущего состояния в объект, flagполе в текущем состоянии переопределяется). Затем я просто устанавливаю состояние somePropertyмоего newStateобъекта.

Мэтью Беркомпас
источник
6

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

Для такого государства

state = {
 contact: {
  phone: '888-888-8888',
  email: 'test@test.com'
 }
 address: {
  street:''
 },
 occupation: {
 }
}

Повторно используемый метод, который я использовал, выглядел бы так.

handleChange = (obj) => e => {
  let x = this.state[obj];
  x[e.target.name] = e.target.value;
  this.setState({ [obj]: x });
};

затем просто передавая имя объекта для каждого вложения, к которому вы хотите обратиться ...

<TextField
 name="street"
 onChange={handleChange('address')}
 />
user2208124
источник
4

Я использовал это решение.

Если у вас есть вложенное состояние, подобное этому:

   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 с прослушивателем событий

<input type="text" onChange={this.handleChange} " name="friendName" />
Альберто Пирас
источник
Подтверждено. Это прекрасно работает, если вы уже имеете дело с уже созданным проектом с вложенными свойствами.
metal_jacke1
4

Создайте копию состояния:

let someProperty = JSON.parse(JSON.stringify(this.state.someProperty))

внести изменения в этот объект:

someProperty.flag = "false"

сейчас обновите состояние

this.setState({someProperty})
Dhakad
источник
setState является асинхронным. Поэтому, пока он обновляется, кто-то другой может вызвать эту функцию и скопировать устаревшую версию состояния.
Авив Бен Шабат
3

Хотя вы спрашивали о состоянии компонента React на основе классов, такая же проблема существует с ловушкой useState. Еще хуже: ловушка useState не принимает частичные обновления. Так что этот вопрос стал очень актуальным, когда был введен хук useState.

Я решил опубликовать следующий ответ, чтобы убедиться, что этот вопрос охватывает более современные сценарии, в которых используется хук useState:

Если у вас есть:

const [state, setState] = useState({ someProperty: { flag: true, otherNestedProp: 1 }, otherProp: 2 })

Вы можете установить вложенное свойство путем клонирования текущего и исправления необходимых сегментов данных, например:

setState(current => { ...current, someProperty: { ...current.someProperty, flag: false } });

Или вы можете использовать библиотеку Immer, чтобы упростить клонирование и исправление объекта.

Или вы можете использовать библиотеку Hookstate (отказ от ответственности: я автор), чтобы просто полностью управлять сложными (локальными и глобальными) данными состояния и повысить производительность (читай: не беспокоиться об оптимизации рендеринга):

import { useStateLink } from '@hookstate/core' 
const state = useStateLink({ someProperty: { flag: true, otherNestedProp: 1 }, otherProp: 2 })

получить поле для рендеринга:

state.nested.someProperty.nested.flag.get()
// or 
state.get().someProperty.flag

установить вложенное поле:

state.nested.someProperty.nested.flag.set(false)

Вот пример Hookstate, где состояние глубоко / рекурсивно вложено в древовидную структуру данных .

Эндрю
источник
2

Два других варианта еще не упомянуты:

  1. Если у вас глубоко вложенное состояние, подумайте, сможете ли вы реструктурировать дочерние объекты, чтобы они находились в корне. Это облегчает обновление данных.
  2. Существует множество удобных библиотек для обработки неизменяемого состояния, перечисленных в документации Redux . Я рекомендую Immer, так как он позволяет вам писать код нерегулярно, но обрабатывает необходимое клонирование за кулисами. Он также замораживает полученный объект, поэтому вы не можете случайно изменить его позже.
Кори Дом
источник
2

Чтобы сделать вещи общими, я работал над ответами @ ShubhamKhatri's и @ Qwerty.

государственный объект

this.state = {
  name: '',
  grandParent: {
    parent1: {
      child: ''
    },
    parent2: {
      child: ''
    }
  }
};

элементы управления вводом

<input
  value={this.state.name}
  onChange={this.updateState}
  type="text"
  name="name"
/>
<input
  value={this.state.grandParent.parent1.child}
  onChange={this.updateState}
  type="text"
  name="grandParent.parent1.child"
/>
<input
  value={this.state.grandParent.parent2.child}
  onChange={this.updateState}
  type="text"
  name="grandParent.parent2.child"
/>

метод updateState

setState как ответ @ ShubhamKhatri

updateState(event) {
  const path = event.target.name.split('.');
  const depth = path.length;
  const oldstate = this.state;
  const newstate = { ...oldstate };
  let newStateLevel = newstate;
  let oldStateLevel = oldstate;

  for (let i = 0; i < depth; i += 1) {
    if (i === depth - 1) {
      newStateLevel[path[i]] = event.target.value;
    } else {
      newStateLevel[path[i]] = { ...oldStateLevel[path[i]] };
      oldStateLevel = oldStateLevel[path[i]];
      newStateLevel = newStateLevel[path[i]];
    }
  }
  this.setState(newstate);
}

setState как ответ @ Qwerty

updateState(event) {
  const path = event.target.name.split('.');
  const depth = path.length;
  const state = { ...this.state };
  let ref = state;
  for (let i = 0; i < depth; i += 1) {
    if (i === depth - 1) {
      ref[path[i]] = event.target.value;
    } else {
      ref = ref[path[i]];
    }
  }
  this.setState(state);
}

Примечание. Эти методы не работают для массивов.

Венугопал
источник
2

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

import produce from 'immer';

<Input
  value={this.state.form.username}
  onChange={e => produce(this.state, s => { s.form.username = e.target.value }) } />

Это должно сработать React.PureComponent(например, сравнение с неглубокими состояниями с помощью React), так как Immerумно использует прокси-объект для эффективного копирования произвольно глубокого дерева состояний. Immer также более безопасен по сравнению с такими библиотеками, как Immutability Helper, и идеально подходит для пользователей Javascript и Typescript.


Полезность Typescript

function setStateDeep<S>(comp: React.Component<any, S, any>, fn: (s: 
Draft<Readonly<S>>) => any) {
  comp.setState(produce(comp.state, s => { fn(s); }))
}

onChange={e => setStateDeep(this, s => s.form.username = e.target.value)}
Стивен Пол
источник
1
stateUpdate = () => {
    let obj = this.state;
    if(this.props.v12_data.values.email) {
      obj.obj_v12.Customer.EmailAddress = this.props.v12_data.values.email
    }
    this.setState(obj)
}
user3444748
источник
1
Я не думаю, что это правильно, так как objэто просто ссылка на состояние, поэтому через него вы мутируете фактический this.stateобъект
Ciprian Tomoiagă
0

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

return (
  <div>
      <h2>Project Details</h2>
      <form>
        <Input label="ID" group type="number" value={this.state.project.id} onChange={(event) => this.setState({ project: {...this.state.project, id: event.target.value}})} />
        <Input label="Name" group type="text" value={this.state.project.name} onChange={(event) => this.setState({ project: {...this.state.project, name: event.target.value}})} />
      </form> 
  </div>
)

Дай мне знать!

Майкл Стоукс
источник
0

Нечто подобное может быть достаточно,

const isObject = (thing) => {
    if(thing && 
        typeof thing === 'object' &&
        typeof thing !== null
        && !(Array.isArray(thing))
    ){
        return true;
    }
    return false;
}

/*
  Call with an array containing the path to the property you want to access
  And the current component/redux state.

  For example if we want to update `hello` within the following obj
  const obj = {
     somePrimitive:false,
     someNestedObj:{
        hello:1
     }
  }

  we would do :
  //clone the object
  const cloned = clone(['someNestedObj','hello'],obj)
  //Set the new value
  cloned.someNestedObj.hello = 5;

*/
const clone = (arr, state) => {
    let clonedObj = {...state}
    const originalObj = clonedObj;
    arr.forEach(property => {
        if(!(property in clonedObj)){
            throw new Error('State missing property')
        }

        if(isObject(clonedObj[property])){
            clonedObj[property] = {...originalObj[property]};
            clonedObj = clonedObj[property];
        }
    })
    return originalObj;
}

const nestedObj = {
    someProperty:true,
    someNestedObj:{
        someOtherProperty:true
    }
}

const clonedObj = clone(['someProperty'], nestedObj);
console.log(clonedObj === nestedObj) //returns false
console.log(clonedObj.someProperty === nestedObj.someProperty) //returns true
console.log(clonedObj.someNestedObj === nestedObj.someNestedObj) //returns true

console.log()
const clonedObj2 = clone(['someProperty','someNestedObj','someOtherProperty'], nestedObj);
console.log(clonedObj2 === nestedObj) // returns false
console.log(clonedObj2.someNestedObj === nestedObj.someNestedObj) //returns false
//returns true (doesn't attempt to clone because its primitive type)
console.log(clonedObj2.someNestedObj.someOtherProperty === nestedObj.someNestedObj.someOtherProperty) 
Eladian
источник
0

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

  constructor(props) {
    super(props);

    this.state = {
      loading: false,
      user: {
        email: ""
      },
      organization: {
        name: ""
      }
    };

    this.handleChange = this.handleChange.bind(this);
  }

Моя handleChangeфункция такая:

  handleChange(e) {
    const names = e.target.name.split(".");
    const value = e.target.type === "checkbox" ? e.target.checked : e.target.value;
    this.setState((state) => {
      state[names[0]][names[1]] = value;
      return {[names[0]]: state[names[0]]};
    });
  }

И убедитесь, что вы называете входные данные соответственно:

<input
   type="text"
   name="user.email"
   onChange={this.handleChange}
   value={this.state.user.firstName}
   placeholder="Email Address"
/>

<input
   type="text"
   name="organization.name"
   onChange={this.handleChange}
   value={this.state.organization.name}
   placeholder="Organization Name"
/>
Elnoor
источник
0

Я делаю вложенные обновления с сокращенным поиском:

Пример:

Вложенные переменные в состоянии:

state = {
    coords: {
        x: 0,
        y: 0,
        z: 0
    }
}

Функция:

handleChange = nestedAttr => event => {
  const { target: { value } } = event;
  const attrs = nestedAttr.split('.');

  let stateVar = this.state[attrs[0]];
  if(attrs.length>1)
    attrs.reduce((a,b,index,arr)=>{
      if(index==arr.length-1)
        a[b] = value;
      else if(a[b]!=null)
        return a[b]
      else
        return a;
    },stateVar);
  else
    stateVar = value;

  this.setState({[attrs[0]]: stateVar})
}

Использование:

<input
value={this.state.coords.x}
onChange={this.handleTextChange('coords.x')}
/>
Эмизаэль Каррера
источник
0

Это мой initialState

    const initialStateInput = {
        cabeceraFamilia: {
            familia: '',
            direccion: '',
            telefonos: '',
            email: ''
        },
        motivoConsulta: '',
        fechaHora: '',
        corresponsables: [],
    }

Хук или вы можете заменить его на состояние (компонент класса)

const [infoAgendamiento, setInfoAgendamiento] = useState(initialStateInput);

Метод для handleChange

const actualizarState = e => {
    const nameObjects = e.target.name.split('.');
    const newState = setStateNested(infoAgendamiento, nameObjects, e.target.value);
    setInfoAgendamiento({...newState});
};

Метод для установки состояния с вложенными состояниями

const setStateNested = (state, nameObjects, value) => {
    let i = 0;
    let operativeState = state;
    if(nameObjects.length > 1){
        for (i = 0; i < nameObjects.length - 1; i++) {
            operativeState = operativeState[nameObjects[i]];
        }
    }
    operativeState[nameObjects[i]] = value;
    return state;
}

Наконец, это вход, который я использую

<input type="text" className="form-control" name="cabeceraFamilia.direccion" placeholder="Dirección" defaultValue={infoAgendamiento.cabeceraFamilia.direccion} onChange={actualizarState} />
Рамиро Карбонелл Дельгадо
источник
0

Если вы используете formik в своем проекте, у него есть простой способ справиться с этим. Вот самый простой способ сделать это с formik.

Сначала установите ваши начальные значения в атрибуте formik initivalues ​​или в реакции. штат

Здесь начальные значения определены в состоянии реакции.

   state = { 
     data: {
        fy: {
            active: "N"
        }
     }
   }

определите выше initialValues ​​для поля formik внутри initiValuesатрибута formik

<Formik
 initialValues={this.state.data}
 onSubmit={(values, actions)=> {...your actions goes here}}
>
{({ isSubmitting }) => (
  <Form>
    <Field type="checkbox" name="fy.active" onChange={(e) => {
      const value = e.target.checked;
      if(value) setFieldValue('fy.active', 'Y')
      else setFieldValue('fy.active', 'N')
    }}/>
  </Form>
)}
</Formik>

Создайте консоль для проверки обновленного состояния stringвместо функции booleanformik, setFieldValueчтобы установить состояние, или воспользуйтесь инструментом отладчика реакции, чтобы увидеть изменения в значениях состояния formik.

Шарад Шарма
источник
0

попробуйте этот код:

this.setState({ someProperty: {flag: false} });
Soumya
источник
Но это удалит любые другие свойства, присутствующие в объекте, на который ссылается, somePropertyи после выполнения вышеуказанного кода flagостанется только свойство
Yashas
0

я видел следующее в книге:

this.setState(state => state.someProperty.falg = false);

но я не уверен, правильно ли это ..

Роса Евы
источник