Як оновити вкладені властивості стану в React


390

Я намагаюся організувати свій стан, використовуючи вкладені властивості на зразок цього:

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

Але такий стан оновлення,

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

не працює. Як це можна зробити правильно?


4
Що означає, що не працює? Це не дуже вдале запитання - що сталося? Будь-які помилки? Які помилки?
Darren Sweeney


16
Відповідь: Не використовувати вкладений стан у React . Прочитайте цю чудову відповідь.
Qwerty

4
Що слід використовувати замість цього?
Яніс Елмеріс

42
Просто не використовувати вкладений стан - це неприйнятна відповідь на те, наскільки широко використовується React сьогодні. Ця ситуація вийде, і розробники потребують відповіді на це.
seraosays

Відповіді:


455

Для 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-helperпакет для оновлення стану.

Дивіться цю відповідь про те, як оновити стан immutability helper.


2
@Ohgodwhy, ні це не є прямим доступом до держави, оскільки {... this.state.someProperty} повертає новий предмет somePropertyі ми його модифікуємо
Shubham Khatri

4
це не спрацювало для мене .. Я використовую реакційну версію @ 15.6.1
Rajiv

5
@Stophface Ми можемо використовувати Лодаша для глибокого клонування, але далеко не кожен включив би цю бібліотеку просто для встановленнядержави
Shubham

15
Чи це не порушує reakctjs.org/docs/… ? Якщо дві зміни вкладеного об'єкта отримані, остання зміна замінить першу. Тож найправильніший спосіб був би: this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}}) Трохи нудний, хоча ...
olejorgenb

2
Я не думаю, що тобі потрібен ...prevState,останній приклад, ти лише somePropertyта його діти
Джемар Джонс

140

Щоб записати його в один рядок

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

12
Рекомендується використовувати оновлення для setState, а не безпосередньо звертатися до цього.state в межах setState. reactjs.org/docs/react-component.html#setstate
Raghu Teja

6
Я вважаю, що це говорить, не читайте this.stateодразу після цього setStateі не сподівайтеся, що він матиме нове значення. Versus дзвінки 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властивість не змінилася. BTW, що безпосередньо виключає об'єкт стану, вважається поганою практикою в цілому.

Тож вам потрібно заново створити весь parentоб’єкт. Але в цьому випадку ми зустрінемо ще одну проблему. Реагент подумає, що всі діти змінили свої цінності і перетворить їх усіх. Звичайно, це не добре для виконання.

Ще можна вирішити цю проблему, записавши складну логіку, shouldComponentUpdate()але я вважаю за краще зупинитися тут і використати просте рішення з короткої версії.


4
Я думаю, що є випадки використання, коли вкладений стан ідеально добре. Наприклад, стан, що динамічно відстежує пари ключ-значення. Дивіться тут , як ви можете оновити стан для тільки одного запису в державі: reactjs.org/docs / ...
ПОРСА

1
Неглибоке порівняння за замовчуванням використовується для чистих компонентів.
Базель Шишані

4
Я хочу створити вівтар на вашу честь і молитися до нього щоранку. Щоб дійти до цієї відповіді, що чудово пояснює ОБОВ'ЯЗКОВЕ дизайнерське рішення, мені знадобилося три дні. Усі намагаються використовувати оператор розповсюдження або робити інші хаки лише тому, що вкладений стан виглядає більш зрозумілим для людини.
Едеф

1
Як ви пропонуєте тоді використовувати React для таких речей, як надання інтерактивного дерева (наприклад, моя компанія має купу датчиків у різних куточках світу, кожен різного типу, з різними властивостями, в різних місцях (80+ ) та КУРСУ вони організовані в ієрархії, оскільки кожне розгортання коштує тисячі доларів і є повноцінним проектом, тому неможливо було б керувати ними без ієрархії). Мені дуже цікаво, як ти б не моделював такі речі, як дерева, як ... дерева, в React.
DanyAlejandro

10
Схоже, React не був створений для представлення нічого, що зберігається як рекурсивна структура нетривіальної глибини. Це трохи невтішно, оскільки представлення дерев з'являється майже в кожному складному додатку (gmail, файлові менеджери, будь-яка система управління документами, ..). Я використовував React для цих речей, але завжди мав реалію React, який вказував усім, що ви робите анти-шаблони, і припускаю, що рішення зберігає глибокі копії дерева у стані кожного вузла (так, 80 копій). Я хотів би побачити не хакітний компонент з видом на дерево, який не порушує жодних правил React.
DanyAlejandro

61

Відмова від відповідальності

Вкладений стан в 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))

Примітка. Це ось оператор Comma ~ MDN , дивіться це в дії тут (пісочниця) .

Це схоже на (хоча це не змінює посилання на стан)

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

Для тонкої різниці в цьому контексті між forceUpdateі setStateдив. Зв'язаний приклад.

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

Увага

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


12
Ви ніде не повинні мутувати будь-який стан реакції. Якщо є вкладені компоненти, які використовують дані з state.nested або state.another, вони не відтворюватимуть, а також їхні діти. Це тому, що об’єкт не змінився, тому React вважає, що нічого не змінилося.
Зерагамба

@ SpyMaster356 Я оновив свою відповідь, щоб вирішити це. Також зауважте, що MobX, наприклад, stateповністю ровить і використовує властивості, які можна спостерігати . React's setState- це просто вбудована зручність, але ви незабаром зрозумієте, що це має свої межі. Використання спеціальних властивостей та інтелектуальне використання forceUpdateдає вам набагато більше. Використовуйте Observables замість стану в компонентах React.
Qwerty

Також я додав приклад react-experiment.herokuapp.com/state-flow (посилання на git вгорі)
Qwerty

Що робити, якщо ви завантажуєте, наприклад, об’єкт з вкладеними атрибутами з бази даних у стан? Як уникнути вкладеного стану в такому випадку?
tobiv

3
@tobiv Краще перетворити дані за виборкою в іншу структуру, але це залежить від використання. Добре мати вкладений об'єкт або масив об’єктів у стані, якщо ви вважаєте їх компактними одиницями деяких даних. Проблема виникає, коли вам потрібно зберігати комутатори інтерфейсу користувача чи інші видимі дані (тобто дані, які повинні негайно змінити вигляд), оскільки вони повинні бути рівними, щоб правильно запустити внутрішні алгоритми реагування на різні реакції. Для змін в інших вкладених об'єктах легко запустити 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);

1
Здається, більш елегантним рішенням тут є: 1) уникнення нудного розповсюдження всередині setState і 2) красиво поставити новий стан всередині setState, уникаючи мутувати безпосередньо всередині setState. Не в останню чергу 3) уникати використання будь-якої бібліотеки для простого завдання
Веб-жінка

Два приклади не рівнозначні. Якщо propertyмістить кілька вкладених властивостей, перший видалить їх, другий не буде. codeandbox.io/s/nameless-pine-42thu
Qwerty

Qwerty, ви праві, дякую за зусилля. Я видалив версію ES6.
eyecandy.dev

Відверто і просто!
Тоні Сепія

Я використав ваше рішення. Мій стан obj невеликий, і він працював чудово. Чи реагуватиме React лише для зміненого стану чи все візуалізує?
Кенджі Ногучі

14

Є багато бібліотек, які допоможуть у цьому. Наприклад, використовуючи помічник-незмінність :

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 :

import {merge} from 'lodash/fp';

const newState = merge(this.state, {
  someProperty: {flag: false},
});

Якщо ви використовуєте lodash, ви, ймовірно, хочете спробувати _.cloneDeepвстановити стан, який буде клонованою копією.
Ісін Лю

@YixingLiu: Я оновив відповідь на використання lodash/fp, тому нам не потрібно турбуватися про мутації.
tokland

Якась перевага вибору mergeабо setфункцій в lodash / fp, крім синтаксису?
Toivo Säwén

Хороша відповідь, можливо, ви хочете додати, що вам потрібно зателефонувати також this.setState(newState)для методів lodash / fp. Інша проблема - те, що лодаш .set(non-fp) має інший порядок аргументів, який мене спонукав на деякий час.
Toivo Säwén

7

Ми використовуємо 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;
}));

За допомогою занурення ви керуєте своїм станом як "нормальним об'єктом". Чара відбувається за сценою з проксі-об'єктами.


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

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

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

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

<TextField
 name="street"
 onChange={handleChange('address')}
 />

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

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 та @ 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

Я дуже серйозно сприймаю занепокоєння, яке вже висловлювалося навколо створення повної копії стану вашого компонента. З урахуванням сказаного, я настійно пропоную Іммера .

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.


Функція утиліти машинопису

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

1
Я не думаю, що це правильно, оскільки objце лише посилання на стан, тому через нього ви this.state
мутуєте

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) 

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"
/>

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

Це моя початкова держава

    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 у своєму проекті, це легкий спосіб впоратися з цими матеріалами. Ось найпростіший спосіб зробити формук.

Спочатку встановіть свої початкові значення всередині атрибута tvork Initivalues ​​або в реакції. держава

Тут вихідні значення визначаються у реагуючому стані

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

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


0

спробуйте цей код:

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

Але це видалить будь-які інші властивості, наявні в об'єкті, на який посилається somePropertyі після виконання вищевказаного коду flag, залишиться лише властивість
Yashas

0

У книзі я побачив наступне:

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

але я не впевнений, чи правильно це ..

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.