programing

this.setState가 예상대로 상태를 Marge하지 않습니다.

starjava 2023. 3. 13. 20:05
반응형

this.setState가 예상대로 상태를 Marge하지 않습니다.

상태는 다음과 같습니다.

this.setState({ selected: { id: 1, name: 'Foobar' } });  

그런 다음 상태를 업데이트합니다.

this.setState({ selected: { name: 'Barfoo' }});

★★setState다음과 같이 말합니다.

{ selected: { id: 1, name: 'Barfoo' } }; 

대신 ID가 사용되고 상태는 다음과 같습니다.

{ selected: { name: 'Barfoo' } }; 

이는 예상된 동작이며 중첩된 상태 개체의 속성을 하나만 업데이트하는 솔루션은 무엇입니까?

생각에는setState()마지

할 수 .this.state.selected setState()에 대해서 「」를 참조해 주세요.

var newSelected = _.extend({}, this.state.selected);
newSelected.name = 'Barfoo';
this.setState({ selected: newSelected });

는 지금까지 _.extend()function 라이브러리에서)을(언더스코어.aribrary)으로의 변경을 방지합니다.selected주의 일부분에 대한 얕은 복사본을 만듭니다.

하나의 은 '보다 '라고 쓰면 될 것 같아요.setStateRecursively()에서 재귀적으로 병합된 후 " 병합"을 호출합니다.replaceState()다음과 같이 합니다.

setStateRecursively: function(stateUpdate, callback) {
  var newState = mergeStateRecursively(this.state, stateUpdate);
  this.replaceState(newState, callback);
}

최근 React.addons에 불변 도우미가 추가되었기 때문에 다음과 같은 작업을 수행할 수 있습니다.

var newState = React.addons.update(this.state, {
  selected: {
    name: { $set: 'Barfoo' }
  }
});
this.setState(newState);

불변 도우미 문서.

많은 답변이 현재 상태를 새로운 데이터 통합의 기준으로 사용하고 있기 때문에 이것이 깨질 수 있다는 점을 지적하고 싶다.상태 변경은 대기열에 저장되며 구성 요소의 상태 개체를 즉시 수정하지 않습니다.따라서 큐가 처리되기 전에 상태 데이터를 참조하면 setState에서 수행한 보류 중인 변경을 반영하지 않는 오래된 데이터가 생성됩니다.문서에서:

setState()는 this.state를 즉시 변환하지 않고 보류 상태의 천이를 만듭니다.이 메서드를 호출한 후 this.state에 액세스하면 기존 값이 반환될 수 있습니다.

즉, setState에 대한 후속 호출에서 "current" 상태를 참조로 사용하는 것은 신뢰할 수 없습니다.예를 들어 다음과 같습니다.

  1. setState에 대한 첫 번째 호출, 상태 객체에 대한 변경 큐잉
  2. setState에 대한 두 번째 호출.상태가 중첩된 개체를 사용하므로 병합을 수행하려고 합니다.setState를 호출하기 전에 현재 상태 개체를 가져옵니다.이 오브젝트는 위의 setState에 대한 첫 번째 호출에서 수행된 큐 변경을 반영하지 않습니다.이는 아직 원래 상태이기 때문에 이제 "stale"로 간주됩니다.
  3. Marge를 실행합니다.결과는 원래 "stale" 상태와 방금 설정한 새로운 데이터가 추가되며 초기 setState 호출로부터의 변경은 반영되지 않습니다.setState 콜은 이 두 번째 변경을 큐잉합니다.
  4. 리액트 프로세스 큐첫 번째 setState 콜이 처리되어 상태가 갱신됩니다.두 번째 setState 콜이 처리되어 상태가 갱신됩니다.두 번째 setState 객체가 첫 번째 setState 객체로 대체되었습니다.또, 그 콜을 발신했을 때에 가지고 있던 데이터가 오래된 것이었기 때문에, 이 두 번째 콜로부터의 변경 후의 오래된 데이터는, 첫 번째 콜의 변경을 클로버 해 버렸습니다.이 데이터는 없어집니다.
  5. 큐가 비어 있는 경우 React는 렌더링 등을 결정합니다.이 시점에서 두 번째 setState 콜에서 이루어진 변경을 렌더링하면 첫 번째 setState 콜이 발생하지 않은 것처럼 됩니다.

현재 상태를 사용할 필요가 있는 경우(예를 들어 데이터를 중첩된 객체에 병합하기 위해), setState는 대체적으로 함수를 개체 대신 인수로 받아들입니다. 함수는 이전 업데이트 후 호출되어 인수로 전달됩니다.이러한 변경은 이전 변경에 따라 보증됩니다.

다른 라이브러리를 설치하고 싶지 않았기 때문에 다른 해결책이 있습니다.

대신:

this.setState({ selected: { name: 'Barfoo' }});

대신 다음을 수행합니다.

var newSelected = Object.assign({}, this.state.selected);
newSelected.name = 'Barfoo';
this.setState({ selected: newSelected });

또는, 코멘트의 @icc97 덕분에, 보다 간결하게, 그러나 거의 가독성이 떨어집니다.

this.setState({ selected: Object.assign({}, this.state.selected, { name: "Barfoo" }) });

또한 이 답변은 @bgannonpl이 위에서 언급한 우려 사항을 위반하지 않습니다.

@bgannonpl 응답에 따라 이전 상태 유지:

Lodash의 예:

this.setState((previousState) => _.merge({}, previousState, { selected: { name: "Barfood"} }));

정상적으로 동작하고 있는지 확인하려면 두 번째 파라미터 함수 콜백을 사용합니다.

this.setState((previousState) => _.merge({}, previousState, { selected: { name: "Barfood"} }), () => alert(this.state.selected));

하였습니다.mergeextend그렇지 않으면 다른 속성이 파기됩니다.

반응 불변성 예:

import update from "react-addons-update";

this.setState((previousState) => update(previousState, {
    selected:
    { 
        name: {$set: "Barfood"}
    }
});

지금으로서는

다음 상태가 이전 상태에 따라 달라지는 경우 대신 업데이터 기능 양식을 사용하는 것이 좋습니다.

https://reactjs.org/docs/react-component.html#setstate, 매뉴얼에 따라 다음을 수행합니다.

this.setState((prevState) => {
    return {quantity: prevState.quantity + 1};
});

이런 상황에 대한 저의 해결책은 다른 답변처럼 불변 도우미를 사용하는 것입니다.

상태를 상세하게 설정하는 것은 일반적인 상황이기 때문에 다음과 같은 믹스인을 작성했습니다.

var SeStateInDepthMixin = {
   setStateInDepth: function(updatePath) {
       this.setState(React.addons.update(this.state, updatePath););
   }
};

되어 있기 .setState더 이상 직접적으로.

입니다.setStateinDepth다음과 같은 방법으로 합니다.

setStateInDepth({ selected: { name: { $set: 'Barfoo' }}})

상세한 것에 대하여는, 다음을 참조해 주세요.

  • React에서의 mixin의 동작에 대해서는, 공식 메뉴얼을 참조해 주세요.
  • 전달된 파라미터의 구문setStateinDepth불변성 헬퍼의 메뉴얼을 참조해 주세요.

저는 es6 클래스를 사용하고 있는데, 탑스테이트에 여러 개의 복잡한 오브젝트가 있고, 메인 컴포넌트를 모듈화하려고 했습니다.그래서 상태를 탑컴포넌트에 유지하면서도 로컬로직이 가능하도록 심플한 클래스 래퍼를 만들었습니다.

래퍼 클래스는 주 구성 요소 상태에 속성을 설정하는 함수를 생성자로 사용합니다.

export default class StateWrapper {

    constructor(setState, initialProps = []) {
        this.setState = props => {
            this.state = {...this.state, ...props}
            setState(this.state)
        }
        this.props = initialProps
    }

    render() {
        return(<div>render() not defined</div>)
    }

    component = props => {
        this.props = {...this.props, ...props}
        return this.render()
    }
}

그런 다음 상위 상태의 각 복잡한 속성에 대해 하나의 StateWraped 클래스를 만듭니다.여기서 컨스트럭터에서 기본 소품을 설정할 수 있습니다.클래스가 초기화되면 기본 소품이 설정됩니다.로컬 상태에 값을 참조하고 로컬 상태를 설정할 수 있습니다.로컬 함수를 참조하고 체인을 통과할 수 있습니다.

class WrappedFoo extends StateWrapper {

    constructor(...props) { 
        super(...props)
        this.state = {foo: "bar"}
    }

    render = () => <div onClick={this.props.onClick||this.onClick}>{this.state.foo}</div>

    onClick = () => this.setState({foo: "baz"})


}

따라서 최상위 컴포넌트는 각 클래스를 최상위 상태 속성, 단순 렌더링 및 여러 컴포넌트 간에 통신하는 함수로 설정하기만 하면 됩니다.

class TopComponent extends React.Component {

    constructor(...props) {
        super(...props)

        this.foo = new WrappedFoo(
            props => this.setState({
                fooProps: props
            }) 
        )

        this.foo2 = new WrappedFoo(
            props => this.setState({
                foo2Props: props
            }) 
        )

        this.state = {
            fooProps: this.foo.state,
            foo2Props: this.foo.state,
        }

    }

    render() {
        return(
            <div>
                <this.foo.component onClick={this.onClickFoo} />
                <this.foo2.component />
            </div>
        )
    }

    onClickFoo = () => this.foo2.setState({foo: "foo changed foo2!"})
}

제 목적상으로는 잘 동작하는 것 같습니다만, 랩된 각 컴포넌트는 자체 상태를 추적하지만 변경될 때마다 상위 컴포넌트의 상태를 업데이트하기 때문에 최상위 컴포넌트에 할당한 속성 상태를 변경할 수는 없습니다.

솔루션

편집: 솔루션은 확산 구문을 사용하는 데 사용됩니다.그 목표는 어떠한 언급도 없이 오브젝트를 만드는 것이었다.prevState,하도록prevState수정되지 않았습니다.하지만 내 방식으로는,prevState가끔 수정되는 것처럼 보였어요.그래서 부작용 없는 완벽한 복제를 위해, 우리는 이제 변환합니다.prevStateJSON으로 이동하고 다시 돌아갑니다.(JSON을 사용하는 아이디어는 MDN에서 나왔습니다.)

주의:

  • 상태 업데이트가 얕다: 한 단계만 깊이가 있다
  • 주(州)가 직접 변이되어서는 안 된다.대신,

순서

  1. 루트 수준 속성 복사본을 만듭니다.state바꾸고 싶은 것
  2. 이 새 개체 변환
  3. 업데이트 개체를 만듭니다.
  4. 업데이트 반환

스텝 3과 스텝4는 1행으로 조합할 수 있습니다.

this.setState(prevState => {
    var newSelected = JSON.parse(JSON.stringify(prevState.selected)) //1
    newSelected.name = 'Barfoo'; //2
    var update = { selected: newSelected }; //3
    return update; //4
});

간단한 예:

this.setState(prevState => {
    var selected = JSON.parse(JSON.stringify(prevState.selected)) //1
    selected.name = 'Barfoo'; //2
    return { selected }; //3, 4
});

이는 React 가이드라인을 잘 따릅니다.비슷한 질문에 대한 Eicksl의 대답에 근거합니다.

ES6 솔루션

처음에 상태를 설정했습니다.

this.setState({ selected: { id: 1, name: 'Foobar' } }); 
//this.state: { selected: { id: 1, name: 'Foobar' } }

상태 객체의 특정 수준에서 속성을 변경하는 중입니다.

const { selected: _selected } = this.state
const  selected = { ..._selected, name: 'Barfoo' }
this.setState({selected})
//this.state: { selected: { id: 1, name: 'Barfoo' } }

.setState한편, 상태 멤버의 갱신은 동시에 행해지지 않을 것으로 예상됩니다. 라이브러리를 합니다.슬라이스 또는 Object.assign)을 사용하거나 전용 라이브러리를 사용합니다.

이것처럼 말이다.NestedLink는 복합 리액트 상태의 처리를 직접 지원합니다.

this.linkAt( 'selected' ).at( 'name' ).set( 'Barfoo' );

「」, 「」에의 도 있습니다.selected ★★★★★★★★★★★★★★★★★」selected.name될 수 .set.

초기 상태를 설정했습니까?

예를 들어 다음과 같은 자체 코드를 사용합니다.

    getInitialState: function () {
        return {
            dragPosition: {
                top  : 0,
                left : 0
            },
            editValue : "",
            dragging  : false,
            editing   : false
        };
    }

현재 작업 중인 앱에서는 이렇게 상태를 설정하고 사용하고 있습니다.는 는는 를 믿는다setState 각자 할 수 있습니다. 이렇게 불렀습니다.

    onChange: function (event) {
        event.preventDefault();
        event.stopPropagation();
        this.setState({editValue: event.target.value});
    },

를 설정할 가 있다는 해 주십시오.이, 테, 트, 테,React.createClassgetInitialState

tmp var를 사용하여 변경합니다.

changeTheme(v) {
    let tmp = this.state.tableData
    tmp.theme = v
    this.setState({
        tableData : tmp
    })
}

언급URL : https://stackoverflow.com/questions/18933985/this-setstate-isnt-merging-states-as-i-would-expect

반응형