जैसा कि मैं अपेक्षा करता हूं, यहसेटसेट राज्यों का विलय नहीं कर रहा है


160

मेरी निम्नलिखित अवस्था है:

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

फिर मैं राज्य को अद्यतन करता हूं:

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

चूंकि setStateमुझे लगता है कि मैं विलय की उम्मीद कर रहा हूं:

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

लेकिन इसके बजाय यह आईडी खाता है और राज्य है:

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

क्या यह अपेक्षित व्यवहार है और एक नेस्टेड स्टेट ऑब्जेक्ट की केवल एक संपत्ति को अपडेट करने का क्या उपाय है?

जवाबों:


143

मुझे लगता setState()है कि पुनरावर्ती विलय नहीं करता है।

आप this.state.selectedनए राज्य के निर्माण के लिए वर्तमान स्थिति के मूल्य का उपयोग कर सकते हैं और फिर setState()उस पर कॉल कर सकते हैं:

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

मैंने समारोह _.extend()समारोह (अंडरस्कोर। Js लाइब्रेरी से) का उपयोग किया है ताकि selectedराज्य के मौजूदा हिस्से में संशोधन करके इसकी उथली प्रतिलिपि बनाई जा सके।

एक और समाधान यह लिखा जाएगा कि setStateRecursively()एक नए राज्य पर पुनरावर्ती विलय होता है और फिर replaceState()इसके साथ कॉल किया जाता है:

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

7
क्या यह कार्य मज़बूती से होता है यदि आप इसे एक से अधिक बार करते हैं या एक मौका प्रतिक्रिया होती है जो रीप्लेस्टेट कॉल को कतारबद्ध करेगा और अंतिम एक जीत होगा?
edoloughlin

111
जो लोग this.setState({ selected: { ...this.state.selected, name: 'barfoo' } })this.setState({ selected: _extends({}, this.state.selected, { name: 'barfoo' }) });
एक्सटेंशन

8
@ यह प्रतिक्रिया के लिए बढ़िया है, अच्छी तरह से मुहावरेदार है और इसकी आवश्यकता नहीं है underscore/ lodash। कृपया इसे अपने उत्तर में बंद करें।
एरिकोस्को

14
this.state जरूरी नहीं कि अप-टू-डेट हो । आप विस्तार विधि का उपयोग करके अप्रत्याशित परिणाम प्राप्त कर सकते हैं। अपडेट फ़ंक्शन को पास setStateकरना सही समाधान है।
Strom

1
@ EdwardD'Souza यह लॉश लाइब्रेरी है। यह आमतौर पर एक अंडरस्कोर के रूप में लागू किया जाता है, उसी तरह jQuery आमतौर पर एक डॉलर के रूप में लागू किया जाता है। (तो, यह _.extend () है।)
mjk

96

इम्यूएबिलिटी हेल्पर्स को हाल ही में React.addons में जोड़ा गया था, इसलिए अब आप कुछ ऐसा कर सकते हैं:

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

अपरिवर्तनीयता सहायक दस्तावेज


7
अद्यतन हटा दिया गया है। facebook.github.io/react/docs/update.html
thedanotto

3
@thedanotto ऐसा लगता है कि React.addons.update()विधि पदावनत है, लेकिन प्रतिस्थापन पुस्तकालय github.com/kolodny/immutability-helper में समतुल्य update()फ़ंक्शन है।
बंगल

37

चूंकि कई उत्तर नए डेटा में विलय के आधार के रूप में वर्तमान स्थिति का उपयोग करते हैं, इसलिए मैं यह बताना चाहता था कि यह टूट सकता है। राज्य परिवर्तन कतारबद्ध हैं, और किसी घटक की वस्तु को तुरंत संशोधित नहीं करते हैं। कतार में संसाधित होने से पहले राज्य डेटा को संदर्भित करना इसलिए आपको बासी डेटा देगा जो आपके द्वारा सेटस्टेट में किए गए लंबित परिवर्तनों को प्रतिबिंबित नहीं करता है। डॉक्स से:

setState () तुरंत इसे बदल नहीं पाता है। इसे स्थिर करें लेकिन एक लंबित राज्य संक्रमण बनाता है। इस पद्धति पर कॉल करने के बाद इस तक पहुँच संभवतया मौजूदा मान को लौटा सकती है।

इसका अर्थ है "चालू" स्थिति का उपयोग करके सेटस्टैट के लिए बाद की कॉल में एक संदर्भ के रूप में अविश्वसनीय है। उदाहरण के लिए:

  1. राज्य ऑब्जेक्ट में परिवर्तन को कतारबद्ध करते हुए, सेटस्टैट पर पहली कॉल करें
  2. दूसरी कॉल सेट करने के लिए। आपका राज्य नेस्टेड ऑब्जेक्ट का उपयोग करता है, इसलिए आप मर्ज करना चाहते हैं। SetState पर कॉल करने से पहले, आपको वर्तमान स्थिति ऑब्जेक्ट मिलता है। यह ऑब्जेक्ट पहले कॉल में सेट किए गए परिवर्तनों को प्रतिबिंबित नहीं करता है, ऊपर सेट करने के लिए, क्योंकि यह अभी भी मूल स्थिति है, जिसे अब "बासी" माना जाना चाहिए।
  3. मर्ज करें। परिणाम मूल है "बासी" राज्य प्लस नया डेटा जो आपने अभी सेट किया है, प्रारंभिक सेटस्टेट कॉल से परिवर्तन प्रतिबिंबित नहीं होते हैं। आपका सेटस्टैट कॉल इस दूसरे परिवर्तन को कतार में खड़ा करता है।
  4. प्रतिक्रिया प्रक्रियाओं कतार। पहले सेटस्टेट कॉल को संसाधित किया जाता है, अद्यतन स्थिति। दूसरा सेटस्टेट कॉल संसाधित किया जाता है, अद्यतन स्थिति। दूसरे सेटस्टैट की वस्तु अब पहले की जगह ले चुकी है, और चूंकि उस कॉल को करते समय आपके पास जो डेटा था, वह बासी हो गया था, इस दूसरी कॉल के संशोधित बासी डेटा ने पहली कॉल में किए गए बदलावों को बंद कर दिया है, जो खो गए हैं।
  5. जब कतार खाली होती है, तो प्रतिक्रिया निर्धारित करती है कि क्या रेंडर करना है आदि। इस बिंदु पर आप दूसरे सेटस्टैट कॉल में किए गए परिवर्तनों को प्रस्तुत करेंगे, और यह ऐसा होगा जैसे कि पहला सेटस्टैट कॉल कभी नहीं हुआ हो।

यदि आपको वर्तमान स्थिति का उपयोग करने की आवश्यकता है (उदाहरण के लिए डेटा को नेस्टेड ऑब्जेक्ट में मर्ज करना), तो सेटस्टैट वैकल्पिक रूप से एक फ़ंक्शन को एक ऑब्जेक्ट के बजाय एक तर्क के रूप में स्वीकार करता है; राज्य में किसी भी पिछले अपडेट के बाद फ़ंक्शन को बुलाया जाता है, और राज्य को एक तर्क के रूप में पारित किया जाता है - इसलिए इसका उपयोग पिछले परिवर्तनों का सम्मान करने के लिए परमाणु परिवर्तनों की गारंटी देने के लिए किया जा सकता है।


5
क्या ऐसा करने का कोई उदाहरण या अधिक डॉक्स है? afaik, कोई भी इसे ध्यान में नहीं रखता है।
जोसेफ.बी

@ डेनिस नीचे मेरे जवाब की कोशिश करो।
मार्टिन डॉसन

मैं नहीं देखता कि समस्या कहाँ है। यदि आप वर्णन कर रहे हैं तो केवल तभी होगा जब आप सेटस्टैट पर कॉल करने के बाद "नए" स्थिति तक पहुंचने का प्रयास करेंगे। यह एक पूरी तरह से अलग समस्या है जिसका ओपी प्रश्न से कोई लेना-देना नहीं है। सेटस्टैट पर कॉल करने से पहले पुराने राज्य तक पहुंचना ताकि आप एक नई राज्य वस्तु का निर्माण कर सकें, कभी भी समस्या नहीं होगी, और वास्तव में ठीक यही है कि रिएक्ट की उम्मीद है।
7

@nextgentech " सेटस्टैट पर कॉल करने से पहले पुराने राज्य तक पहुंच बनाना ताकि आप एक नई राज्य वस्तु का निर्माण कर सकें कभी कोई समस्या नहीं होगी" - आप गलत हैं। जब आप इसे एक्सेस करते हैं, तो हम इसके आधार पर ओवरराइटिंग स्टेट के बारे में बात कर रहे हैं this.state, जो बासी (या बल्कि, लंबित कतार अपडेट) हो सकता है।
मैडब्रेक्स

1
@Madbreaks ठीक है, हाँ, आधिकारिक डॉक्स को फिर से पढ़ने पर मुझे लगता है कि यह निर्दिष्ट किया गया है कि आपको पिछली अवस्था के आधार पर मज़बूती से अद्यतन स्थिति के लिए सेटस्टैट के फ़ंक्शन रूप का उपयोग करना होगा। उस ने कहा, मुझे कभी भी समस्या नहीं हुई है यदि एक ही फ़ंक्शन में कई बार सेटस्टेट को कॉल करने का प्रयास नहीं किया गया है। प्रतिक्रियाओं के लिए धन्यवाद जो मुझे फिर से चश्मा पढ़ने के लिए मिला।
नेकेन्जिटेक

10

मैं एक और पुस्तकालय स्थापित नहीं करना चाहता था, इसलिए यहाँ एक और समाधान है।

के बजाय:

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 ऊपर उल्लिखित है।


अपवोट, चेक करें। बस सोच रहा था कि ऐसा क्यों नहीं है? this.setState({ selected: Object.assign(this.state.selected, { name: "Barfoo" }));
जस्टस रोमीजन

1
@JustusRomijn दुर्भाग्य से तब आप वर्तमान स्थिति को सीधे संशोधित करेंगे। इनसे Object.assign(a, b)विशेषताएँ लेता है b, सम्मिलित करता है / उन्हें अधिलेखित करता है aऔर फिर लौटता है a। यदि आप राज्य को सीधे संशोधित करते हैं तो प्रतिक्रियाशील लोगों को अपरिपक्वता मिलती है।
रयान

बिल्कुल सही। बस ध्यान दें कि यह केवल उथले कॉपी / असाइन करने के लिए काम करता है।
मैडब्रेक्स

1
@JustusRomijn आप के अनुसार यह कर सकते हैं MDN असाइन एक पंक्ति में आप जोड़ना यदि {}पहले तर्क के रूप में: this.setState({ selected: Object.assign({}, this.state.selected, { name: "Barfoo" }) });। यह मूल स्थिति को उत्परिवर्तित नहीं करता है।
icc97

@ icc97 अच्छा लगा! मैंने अपने जवाब में उसे जोड़ा।
रयान

8

@Bgannonpl उत्तर के आधार पर पिछली स्थिति का संरक्षण:

लोदाश उदाहरण:

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

यह जांचने के लिए कि यह ठीक से काम कर रहा है, आप दूसरे पैरामीटर फ़ंक्शन कॉलबैक का उपयोग कर सकते हैं:

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

मैंने उपयोग किया mergeक्योंकि extendअन्य गुणों को अन्यथा छोड़ देता है।

प्रतिक्रिया की अपरिहार्यता का उदाहरण:

import update from "react-addons-update";

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

2

इस तरह की स्थिति के लिए मेरा समाधान का उपयोग करना है, जैसा कि एक अन्य उत्तर में बताया गया है, इम्यूटेबिलिटी हेल्पर्स

चूंकि राज्य को गहराई से स्थापित करना एक सामान्य स्थिति है, मैंने फॉलिंग मिक्सिन बनाया है:

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

यह मिश्रण मेरे अधिकांश घटकों में शामिल है और मैं आमतौर पर setStateसीधे उपयोग नहीं करता हूं।

इस मिश्रण के साथ, आपको वांछित प्रभाव को प्राप्त करने के लिए सभी कार्य करने की आवश्यकता है, फ़ंक्शन setStateinDepthको निम्नलिखित तरीके से कॉल करना है :

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

अधिक जानकारी के लिए:


देर से उत्तर के लिए क्षमा करें, लेकिन आपका मिक्सिन उदाहरण दिलचस्प लगता है। हालाँकि, अगर मेरे पास 2 नेस्टेड स्टेट्स हैं, उदाहरण के लिए this.state.test.one और this.state.test.two। क्या यह सही है कि इनमें से केवल एक को अपडेट किया जाएगा और दूसरे को हटा दिया जाएगा? जब मैं पहले एक को अपडेट करता हूं, अगर दूसरा मैं राज्य में हूं, तो इसे हटा दिया जाएगा ...
mamruoc

hy @mamruoc, this.state.test.twoआपके द्वारा अपडेट this.state.test.oneकरने के बाद भी रहेगा setStateInDepth({test: {one: {$set: 'new-value'}}})। मैं अपने सभी घटकों में इस कोड का उपयोग करता हूं ताकि रिएक्ट के सीमित setStateफ़ंक्शन के आसपास हो सके ।
डोरियन

1
मुझे इसका हल मिल गया। मैंने this.state.testएक ऐरे के रूप में पहल की । ऑब्जेक्ट्स में परिवर्तन, इसे हल किया।
ममरूक

2

मैं 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()
    }
}

फिर शीर्ष राज्य पर प्रत्येक जटिल संपत्ति के लिए, मैं एक StateWrapped क्लास बनाता हूं। आप यहां कंस्ट्रक्टर में डिफ़ॉल्ट प्रॉप्स सेट कर सकते हैं और उन्हें तब सेट किया जाएगा जब क्लास को आरंभीकृत किया जाता है, आप मानों के लिए स्थानीय स्थिति का उल्लेख कर सकते हैं और स्थानीय राज्य सेट कर सकते हैं, स्थानीय फ़ंक्शन का उल्लेख कर सकते हैं, और इसे चेन को पास कर सकते हैं:

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

मेरे उद्देश्यों के लिए काफी अच्छी तरह से काम करने लगता है, हालांकि आप उन गुणों की स्थिति को नहीं बदल सकते हैं जो आप शीर्ष स्तर के घटक में लिपटे घटकों को देते हैं क्योंकि प्रत्येक लिपटे घटक अपने स्वयं के राज्य को ट्रैक कर रहे हैं लेकिन शीर्ष घटक पर स्थिति को अपडेट कर रहे हैं हर बार यह बदल जाता है।


2

जैसे कि अभी,

यदि अगला राज्य पिछली स्थिति पर निर्भर करता है, तो हम इसके बजाय अपडेटर फंक्शन फॉर्म का उपयोग करने की सलाह देते हैं:

प्रलेखन के अनुसार https://reactjs.org/docs/react-component.html#setstate , का उपयोग कर:

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

प्रशस्ति पत्र / लिंक के लिए धन्यवाद, जो अन्य उत्तरों से गायब था।
मैडब्रेक्स

1

उपाय

संपादित करें: यह समाधान प्रसार सिंटैक्स का उपयोग करने के लिए उपयोग किया जाता है । लक्ष्य को बिना किसी संदर्भ के एक वस्तु बना दिया गया था prevState, ताकि prevStateइसे संशोधित नहीं किया जा सके। लेकिन मेरे उपयोग में, prevStateकभी-कभी संशोधित किया गया। तो, साइड इफेक्ट्स के बिना सही क्लोनिंग के लिए, हम अब prevStateJSON में कनवर्ट करते हैं , और फिर वापस। (JSON का उपयोग करने की प्रेरणा एमडीएन से आई है ।)

याद है:

कदम

  1. की जड़-स्तरीय संपत्ति की एक प्रति बनाएँstate जिसे आप बदलना चाहते हैं
  2. इस नई वस्तु को म्यूट करें
  3. एक अद्यतन ऑब्जेक्ट बनाएँ
  4. अपडेट लौटाएं

चरण 3 और 4 को एक लाइन पर जोड़ा जा सकता है।

उदाहरण

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

यह रिएक्ट दिशानिर्देशों का अच्छी तरह से पालन करता है। इसी तरह के एक प्रश्न के उत्तर पर ईक्स् ट के आधार पर ।


1

ईएस 6 समाधान

हमने शुरू में राज्य निर्धारित किया था

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

0

रिएक्ट राज्य पुनरावर्ती मर्ज का प्रदर्शन नहीं करता है, setStateजबकि उम्मीद करता है कि एक ही समय में इन-प्लेस स्टेट मेंबर अपडेट नहीं होंगे। आपको या तो संलग्न ऑब्जेक्ट्स को कॉपी करना होगा / खुद को ऐरे से (array.slice या Object.assign के साथ) या समर्पित लाइब्रेरी का उपयोग करना होगा।

इस तरह। NestedLink सीधे यौगिक प्रतिक्रिया स्थिति से निपटने का समर्थन करता है।

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

इसके अलावा, के लिए लिंक selectedया selected.nameएक भी आधार के रूप में हर जगह से पारित कर दिया और साथ वहाँ संशोधित किया जा सकता set


-2

क्या आपने प्रारंभिक अवस्था निर्धारित की है?

मैं उदाहरण के लिए अपने स्वयं के कोड का उपयोग करूंगा:

    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.createClassकि आपको उस फ़ंक्शन के भीतर स्थिति सेट करना है जिसे आपने कॉल किया थाgetInitialState


1
क्या मिश्रण आप अपने घटक में उपयोग कर रहे हैं? यह मेरे लिए काम नहीं करता है
सचिन

-3

मैं बदलने के लिए tmp var का उपयोग करता हूँ।

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

2
यहाँ tmp.theme = v राज्य को बदल रहा है। तो यह अनुशंसित तरीका नहीं है।
अल्मोराल्सलोपेज़

1
let original = {a:1}; let tmp = original; tmp.a = 5; // original is now === Object {a: 5} ऐसा न करें, भले ही इसने आपको अभी तक समस्या न दी हो।
क्रिस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.