반응 : setState를 사용하여 state.item [1]을 상태로 업데이트하는 방법은 무엇입니까?


259

사용자가 자신의 양식을 디자인 할 수있는 앱을 만들고 있습니다. 예를 들어 필드 이름과 포함해야 할 다른 열의 세부 사항을 지정하십시오.

컴포넌트는 여기 에서 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
이 질문에 대해 선택한 답변은 무엇입니까?
Braian Mellor

답변:


126

update불변 도우미를 사용할 수 있습니다 .

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

또는을 shouldComponentUpdate()사용하여 수명주기 방법 에서이 항목의 변경 사항을 감지하지 못하는 경우 ===상태를 직접 편집하고 구성 요소를 다시 렌더링하도록 할 수 있습니다. 이는 @limelights의 답변과 동일합니다. 객체를 상태에서 벗어나 편집합니다.

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

편집 후 추가 :

콜백 함수를 상태 유지 상위에서 상태 변경을 트리거해야하는 하위 컴포넌트로 전달하는 방법에 대한 예제는 반응 학습에서 단순 컴포넌트 통신 학습을 확인하십시오 .


125

이 스레드에는 많은 잘못된 정보가 있으므로 도우미 라이브러리없이 수행 할 수있는 방법은 다음과 같습니다.

handleChange: function (e) {
    // 1. Make a shallow copy of the items
    let items = [...this.state.items];
    // 2. Make a shallow copy of the item you want to mutate
    let item = {...items[1]};
    // 3. Replace the property you're intested in
    item.name = 'newName';
    // 4. Put it back into our array. N.B. we *are* mutating the array here, but that's why we made a copy first
    items[1] = item;
    // 5. Set the state to our new copy
    this.setState({items});
},

원하는 경우 2 단계와 3 단계를 결합 할 수 있습니다.

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

또는 한 줄로 모든 것을 할 수 있습니다.

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

참고 : items배열을 만들었습니다 . OP는 개체를 사용했습니다. 그러나 개념은 동일합니다.


터미널 / 콘솔에서 진행중인 작업을 확인할 수 있습니다.

 node
> items = [{name:'foo'},{name:'bar'},{name:'baz'}]
[ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ]
> clone = [...items]
[ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ]
> item1 = {...clone[1]}
{ name: 'bar' }
> item1.name = 'bacon'
'bacon'
> clone[1] = item1
{ name: 'bacon' }
> clone
[ { name: 'foo' }, { name: 'bacon' }, { name: 'baz' } ]
> items
[ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ] // good! we didn't mutate `items`
> items === clone
false // these are different objects
> items[0] === clone[0]
true // we don't need to clone items 0 and 2 because we're not mutating them (efficiency gains!)
> items[1] === clone[1]
false // this guy we copied

4
@TranslucentCloud 아, 그래, 도우미 메소드는 확실히 훌륭하지만 모든 사람들이 후드 아래에서 무슨 일이 일어나고 있는지 알아야한다고 생각합니다 :-)
mpen

2 단계와 3 단계가 필요한 이유는 무엇입니까? 첫 번째 단계 후에 복제본을 직접 돌연변이시킬 수없는 이유는 무엇입니까?
Evmorov

1
@Evmorov 딥 클론이 아니기 때문에. 1 단계는 내부의 객체가 아니라 어레이를 복제하는 것입니다. 다시 말해서, 새로운 배열 안에있는 각 객체는 여전히 메모리의 기존 객체를 "지점"합니다. items[0] === clone[0]맨 아래 터미널 예제 의 비트 도 참조하십시오 . 트리플 =은 객체가 같은 것을 참조하는지 확인합니다.
mpen December

배열의 항목 색인을 모르면 더 복잡해집니다.
Ian Warburton

@IanWarburton 실제로는 아닙니다. items.findIndex()그것의 짧은 작업을해야합니다.
mpen

92

잘못된 방법!

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

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

의견에서 많은 우수한 개발자가 지적한 것처럼 상태를 변경하는 것은 잘못되었습니다!

이것을 알아내는 데 시간이 걸렸습니다. 위는 작동하지만 React의 힘을 빼앗습니다. 예를 들어 componentDidUpdate직접 수정 되었기 때문에이 업데이트를 업데이트로 볼 수 없습니다.

그래서에게 올바른 방법은 다음과 같습니다

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

25
"const"를 사용하고 있기 때문에 ES6?
nkkollaw

49
items[1].role = e.target.value돌연변이 상태를 직접 호출하지 않습니까?
antony

28
당신은 상태를 변경하고 있습니다. 이것은 큰 응용 프로그램에서 많은 고통을 줄 수 있습니다.
ncubica

8
@MarvinVK, 당신의 대답은 "그래서 모범 사례는 다음과 같습니다."라고 말한 다음 "this.forceUpdate ();"를 사용합니다. setState ()로 덮어 쓸 수 있으므로 권장되지 않습니다 ( facebook.github.io/react/docs/react-component.html#state 참조) . 미래 독자들에게 혼동되지 않도록 이것을 변경하는 것이 가장 좋습니다.
James Z.

8
많은 주석이 편집으로 인해 관련이 없으며 올바른 방법 이 실제로 올바른 방법 임을 지적하고 싶었습니다 .
heez

50

바닐라 자바 스크립트의 : 일반적으로 세 가지 방법이 사용의 상태, 반작용에 깊이 중첩 된 객체 / 변수를 수정하려면 Object.assign, 불변성-도우미cloneDeepLodash을 .

이것을 달성하기 위해 덜 인기있는 다른 타사 라이브러리도 많이 있지만이 답변에서는이 세 가지 옵션 만 다룰 것입니다. 또한 배열 확산과 같은 일부 추가 바닐라 JavaScript 메소드가 존재하지만 (예 : @mpen의 답변 참조) 직관적이지 않고 사용하기 쉽고 모든 상태 조작 상황을 처리 할 수 ​​없습니다.

답변에 대한 최고 투표 의견에서 셀 수없이 많은 시간이 지적 되었 듯이, 저자는 국가의 직접적인 돌연변이를 제안 합니다 . 이것은 유비쿼터스 리 액트 안티 패턴으로 필연적으로 원치 않는 결과를 초래합니다. 올바른 방법을 배우십시오.

널리 사용되는 세 가지 방법을 비교해 봅시다.

이 상태 객체 구조가 주어지면 :

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복사 할 수 없습니다 .innerMostnewOuterinnerMost

당신은 여전히 복사 할 수 inner위의 예처럼,하지만 지금은 객체와 이후 되지 원시의 기준 에서 newOuter.inner받는 복사됩니다 outer.inner우리가 지방으로 끝날 것이라는 점을 대신하는 수단 newOuter을 직접 상태에서 객체에 연결된 객체 .

즉,이 경우 로컬에서 생성 된 돌연변이가 실제로 동일한 상태가 되었기 때문에 (상태에서) 객체에 newOuter.inner직접 영향을 미칩니다 outer.inner(컴퓨터의 메모리에서).

Object.assign 따라서 가장 기본적인 멤버가 기본 유형의 값을 보유하는 비교적 단순한 단일 레벨 심층 구조가있는 경우에만 작동합니다.

업데이트해야하는 더 깊은 개체 (2 단계 이상)가있는 경우을 사용하지 마십시오 Object.assign. 상태를 직접 변경할 위험이 있습니다.

2. Lodash의 cloneDeep

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>

Lodash의 cloneDeep 은 사용하기가 더 간단합니다. 딥 클로닝을 수행 하므로 다중 레벨 객체 또는 배열이 상당히 복잡한 상태 인 경우 강력한 옵션입니다. 그냥 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 cloneDeep가 현명한 선택입니다.

고급 기능 이 필요한 경우 , 즉 상태 구조가 복잡하고 모든 종류의 작업을 수행해야하는 immutability-helper경우 상태 조작에 사용할 수있는 매우 고급 도구입니다.

... 또는 정말로 이것을해야합니까?

복잡한 데이터를 React의 상태로 보유하고 있다면 다른 방법으로 처리하는 것이 좋습니다. React 컴포넌트에서 복잡한 상태 객체를 바로 설정하는 것은 간단한 작업이 아니며 다른 접근 방식에 대해 강력히 제안하는 것이 좋습니다.

복잡한 데이터를 Redux 스토어에 보관하지 않고 리듀서 및 / 또는 sagas를 사용하여 설정하고 선택기를 사용하여 액세스하는 것이 좋습니다.


Object.assign깊은 복사를 수행하지 않습니다. if 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이 변경되지 않습니다 (it 'll stay {d: 1}). 어쨌든 좋은 캐치, 나는 대답을 즉시 업데이트 할 것입니다.
신경 전달 물질

당신이 정의한 것은 실제로는 shallow copy아닙니다 deep copy. 무슨 shallow copy뜻 인지 혼동하기 쉽습니다 . 에서 shallow copy, a !== b하지만 소스 객체에서 각 키에 대해 a,a[key] === b[key]
무단

예, Object.assign대답의 얕은 부분 을 명시 적으로 언급했습니다 .
신경 전달 물질

JSON.parse(JSON.stringify(object))또한 딥 클론의 변형입니다. cloneDeep그래도 성능이 좋지 않습니다. measurethat.net/Benchmarks/Show/2751/0/…
tylik

35

나는 같은 문제가 있었다. 작동하는 간단한 해결책이 있습니다!

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

11
@TranslucentCloud-이것은 확실히 직접적인 돌연변이가 아닙니다. 원래 배열을 복제하고 수정 한 다음 복제 된 배열을 사용하여 상태를 다시 설정했습니다.
vsync

@vsync 예, 이제 원래 답변을 편집 한 후에는 이것이 전혀 돌연변이가 아닙니다.
신경 전달 물질

2
@TranslucentCloud-이전에도 편집 내용과 관련이 없습니다. 태도에 대해 감사드립니다. @Jonas {는 내가 고친 대괄호 대신 중괄호를 사용하여 그의 대답에서 간단한 실수를했다
vsync

31

setState 의 React 문서에 따르면 Object.assign다른 답변에서 제안한대로 사용 하는 것이 이상적이지 않습니다. setState의 비동기 동작 특성으로 인해이 기술을 사용한 후속 호출은 이전 호출을 무시하여 원하지 않는 결과를 초래할 수 있습니다.

대신 React 문서 setState는 이전 상태에서 작동 하는 업데이터 양식을 사용하는 것이 좋습니다 . React가 상태 불변성을 보존해야하기 때문에 배열이나 객체 업데이트 할 때 새로운 배열이나 객체반환해야합니다 . ES6 구문의 spread 연산자를 사용하여 배열을 얕게 복사하고 주어진 배열 인덱스에서 객체의 속성을 만들거나 업데이트하면 다음과 같습니다.

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

2
ES6을 사용하는 경우 적절한 답변입니다. @Jonas가 답변 한 것은 인라인입니다. 그러나 설명으로 인해 눈에.니다.
Sourabh

그렇습니다.이 코드는 완벽하게 훌륭하고 아주 간단한 코드입니다.
Shoeb Mirza

29

먼저 원하는 항목을 가져 와서 해당 개체에서 원하는 것을 변경 한 다음 상태로 다시 설정하십시오. getInitialState키가있는 객체를 사용하면 객체를 전달하여 상태를 사용하는 방법이 훨씬 쉬워집니다.

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

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

3
아냐 Uncaught TypeError: Cannot read property 'items' of null.
martins

아닙니다. 귀하의 오류는 아마도 귀하의 방식에 기인합니다 getInitialState.
Henrik Andersson

10
@HenrikAndersson 뭔가가 귀하의 예에 맞지 않는 것 같습니다. items어디에도 정의되어 있지 않습니다.
Edward D' Souza 2016 년

1
@ EdwardD'Souza 당신은 절대적으로 맞습니다! 내 대답은 그것이 어떻게 정의되고 사용되어야 하는지를 보여주는 것입니다. 요청자 코드가 설정되는 방식은 원하는 방식으로 작동하지 않으므로 키가 필요한 객체가 필요합니다.
Henrik Andersson

7
이것은 일반적인 React 안티 패턴이며 item, 상태 자체 this.state.items[1]변수에 대한 참조를 할당 합니다. 그런 다음 item( item.name = 'newName') 을 수정 하여 상태를 직접 변경하므로 권장하지 않습니다. 귀하의 예에서는 this.setState({items: items})state가 이미 직접 변경되었으므로을 호출 할 필요조차 없습니다 .
신경 전달 물질

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다른 것을 의미 했습니까 ?
Buh Buh

나는 이것을 테스트했지만 한 줄이 빠져있다. 위에는 items[1] = item;줄이 있어야합니다 items = this.state.items;. 내 자바 스크립트가 녹슬고 내 집 프로젝트에 대한 반응을 배우고 있다는 것을 알고 있으므로 이것이 좋거나 나쁘지 않습니다 :-)
Greg0ry

4

정말 간단합니다.

먼저 전체 항목 객체를 상태에서 가져 와서 원하는대로 항목 객체의 일부를 업데이트 한 다음 setState를 통해 전체 항목 객체를 다시 상태로 놓습니다.

handleChange: function (e) {
  items = Object.assign(this.state.items); // Pull the entire items object out. Using object.assign is a good idea for objects.
  items[1].name = 'newName'; // update the items object as needed
  this.setState({ items }); // Put back in state
}

"원시 유형의 값을 보유하는 가장 안쪽의 멤버가있는 비교적 단순한 1 단계의 딥 스테이트 구조가있는 경우 Object.assign이 작동합니다."
신경 전달 물질

3

위의 옵션 중 어느 것도 나에게 이상적이지 않으므로 map을 사용했습니다.

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설정되어 있는지 볼 수 없습니다 .
신경 전달 물질

배열에서 맵과 비교가 성능에 끔찍하지 않습니까?
Natassia Tavares

@NatassiaTavares .. 무엇? 어쩌면 당신은과 혼동 fo of또는 forEach지도가 가장 빠른 것입니다.
Deano

1

놀랍게도 어려운 것을 발견했으며 ES6 확산 마법 중 어느 것도 예상대로 작동하지 않는 것 같습니다. 레이아웃 목적으로 렌더링 된 요소 속성을 얻기 위해 이와 같은 구조를 사용하고있었습니다.

이 간단한 예제에서 가장 간단한 update방법을 사용하여 발견했습니다 immutability-helper.

constructor(props) {
    super(props)
    this.state = { values: [] }
    this.updateContainerState = this.updateContainerState.bind(this)
  }

updateContainerState(index, value) {
    this.setState((state) => update(state, { values: { [index]: { $set: value } } }))
  }

https://github.com/kolodny/immutability-helper#computed-property-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
이것은 기본적으로 1 년 전에 게시 된 다른 답변 과 동일합니다.
CertainPerformance


0

함수 핸들 변경을 이동하고 인덱스 매개 변수를 추가합니다.

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

동적 양식 구성 요소에 전달하고이를 소품으로 PopulateAtCheckboxes 구성 요소에 전달하십시오. 아이템을 반복 할 때 아래와 같이 추가 카운터 (아래 코드에서 index라고 함)를 포함시켜 핸들 변경에 전달할 수 있습니다.

{ 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

또는 동적으로 생성 된 목록이 있고 색인을 모르지만 키 또는 ID 만있는 경우 :

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 function-이 함수는 구성 요소 색인을 기반으로 배열에 데이터를 넣습니다.
<SubObjectForm setData={this.setSubObjectData}                                                            objectIndex={index}/>

이 SubObjectForm이 사용되는 위치에 따라 {index}를 전달할 수 있습니다.

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");
   }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.