[javascript] React에서 중첩 상태 속성을 업데이트하는 방법

다음과 같이 중첩 속성을 사용하여 상태를 구성하려고합니다.

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

하지만 이런 상태를 업데이트하면

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

작동하지 않습니다. 어떻게 이것을 올바르게 할 수 있습니까?



답변

하기 위해 setState내가 setState를 중첩 업데이트를 처리하지 않습니다 생각하는 중첩 된 객체에 대해 당신이 접근 아래에 따를 수 있습니다.

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

아이디어는 더미 객체를 생성하여 작업을 수행 한 다음 구성 요소의 상태를 업데이트 된 객체로 교체하는 것입니다.

이제 스프레드 연산자는 객체의 하나의 레벨 중첩 복사본 만 만듭니다. 상태가 다음과 같이 고도로 중첩 된 경우 :

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

각 수준에서 스프레드 연산자를 사용하여 state를 설정할 수 있습니다.

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

그러나 위의 구문은 상태가 점점 더 중첩 될 때마다 추악합니다. 따라서 사용하는 것이 좋습니다 immutability-helper 패키지를 사용하여 상태를 업데이트하는 .

로 상태를 업데이트하는 방법에 대한이 답변을 참조하십시오 immutability helper.


답변

한 줄에 쓰려면

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


답변

때로는 직접 답변이 가장 좋은 답변이 아닙니다. 🙂

짧은 버전 :

이 코드

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 개체 합니다. 그러나이 경우 우리는 다른 문제를 만날 것입니다. React는 모든 어린이가 자신의 가치를 바꾸고 다시 렌더링 할 것이라고 생각합니다. 물론 성능에는 좋지 않습니다.

복잡한 논리를 작성 하여이 문제를 해결할 수는 shouldComponentUpdate()있지만 여기서 멈추고 짧은 버전의 간단한 솔루션을 사용하는 것이 좋습니다.


답변

기권

React의 중첩 상태가 잘못 설계되었습니다.

이 훌륭한 답변을 읽으십시오 .

 

이 답변 뒤에 추론 :

React의 setState는 기본 제공 편의 기능이지만 제한이 있음을 곧 깨닫게됩니다. 사용자 정의 속성을 사용하고 forceUpdate를 지능적으로 사용하면 훨씬 더 많은 것을 얻을 수 있습니다. 예 :

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

예를 들어 MobX는 상태를 완전히 도랑 화하고 사용자 지정 관찰 가능 속성을 사용합니다.
React 컴포넌트에서 상태 대신 Observables를 사용하십시오.

 


당신의 불행에 대한 답은 – 여기 예 참조

중첩 속성을 업데이트하는 또 다른 짧은 방법이 있습니다.

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

이 컨텍스트 사이의 미묘한 차이 forceUpdatesetState 연결된 예를 참조.

물론 이것은 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에서 소품이 전파되지 않아서 다시 렌더링되지 않았다는 것을 알 수 있습니다 .


답변

ES2015를 사용하는 경우 Object.assign에 액세스 할 수 있습니다. 다음과 같이 사용하여 중첩 된 객체를 업데이트 할 수 있습니다.

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

업데이트 된 속성을 기존 속성과 병합하고 반환 된 개체를 사용하여 상태를 업데이트합니다.

편집 : carkod가 지적한 것처럼 상태가 직접 변경되지 않도록 할당 함수에 대상으로 빈 객체를 추가했습니다.


답변

const newState = Object.assign({}, this.state);
newState.property.nestedProperty = "new value";
this.setState(newState);


답변

이를 돕기 위해 많은 라이브러리가 있습니다. 예를 들어, 불변성 헬퍼 사용 :

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