प्रतिक्रिया: सेटस्टैट का उपयोग करके राज्य में अद्यतन कैसे करें।


258

मैं एक ऐसा ऐप बना रहा हूं, जहां उपयोगकर्ता अपना खुद का फॉर्म डिजाइन कर सकता है। उदा। फ़ील्ड का नाम और विवरण जिसमें अन्य कॉलम शामिल होने चाहिए।

घटक यहाँ 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
इस प्रश्न के लिए आपका चयनित उत्तर क्या है?
ब्रायन मेलर

जवाबों:


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एक सरणी बनाई है । ओपी ने एक वस्तु का उपयोग किया। हालांकि, अवधारणाएं समान हैं।


आप देख सकते हैं कि आपके टर्मिनल / कंसोल में क्या चल रहा है:

 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 ओह, हाँ, सहायक तरीके निश्चित रूप से अच्छे हैं, लेकिन मुझे लगता है कि हर किसी को पता होना चाहिए कि हुड के नीचे क्या चल रहा है :-)
एमपीएन

हमें चरण 2 और 3 की आवश्यकता क्यों है? हम सीधे पहले चरण के बाद क्लोन को म्यूट क्यों नहीं कर सकते हैं?
एवरमॉव

1
@Evmorov क्योंकि यह एक गहरा क्लोन नहीं है। चरण 1 केवल सरणी को क्लोन करना है, न कि वस्तुओं को अंदर। दूसरे शब्दों में, नए सरणी के अंदर प्रत्येक ऑब्जेक्ट अभी भी "स्मृति में मौजूदा वस्तुओं" को इंगित करता है - एक को बदलना दूसरे को (वे एक और एक ही हैं) को म्यूट करेंगे। items[0] === clone[0]तल पर मेरे टर्मिनल उदाहरण में बिट भी देखें । ट्रिपल =जाँचता है कि क्या ऑब्जेक्ट एक ही चीज़ को संदर्भित करते हैं।
एमपीएन

यदि आप आइटम के इंडेक्स को सरणी में नहीं जानते हैं तो अधिक जटिल हो जाता है।
इयान वॉर्बर्टन

@IanWarburton वास्तव में नहीं। items.findIndex()उस के कम काम करना चाहिए।
मपेन

92

गलत रास्ता!

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

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

जैसा कि टिप्पणियों में बहुत सारे बेहतर डेवलपर्स ने कहा है: राज्य को बदलना गलत है!

यह पता लगाने के लिए मुझे कुछ समय लगा। ऊपर काम करता है लेकिन यह प्रतिक्रिया की शक्ति को दूर ले जाता है। उदाहरण के लिए componentDidUpdateइसे अपडेट के रूप में नहीं देखेंगे क्योंकि यह सीधे संशोधित है।

तो सही तरीका होगा:

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

25
ES6 सिर्फ इसलिए कि आप "const" का उपयोग कर रहे हैं?
nkkollaw

49
items[1].role = e.target.valueसीधे राज्य को कॉल नहीं कर रहा है ?
एंटनी

28
आप राज्य को बदल रहे हैं, यह पूरी तरह से राज्य को बनाए रखने के विचार के खिलाफ है, जैसा कि सुझाव दिया गया है। यह आपको एक बड़े एप्लिकेशन में बहुत दर्द दे सकता है।
ncubica

8
@MarvinVK, आपका उत्तर कहता है "तो सबसे अच्छा अभ्यास होगा:" इसके बाद "this.forceUpate ()" का उपयोग करें; जिसे अनुशंसित नहीं किया गया है क्योंकि इसे setState () द्वारा अधिलेखित किया जा सकता है, facebook.github.io/react/docs/react-component.html#state देखें । इसे बदलने के लिए सबसे अच्छा है ताकि यह भविष्य के पाठकों के लिए भ्रमित न हो।
जेम्स जेड

7
बस यह बताना चाहता था कि बहुत सारी टिप्पणियां संपादन के कारण अब अप्रासंगिक हैं और सही तरीका वास्तव में सही तरीका है।
हेज

50

वेनिला जावास्क्रिप्ट का: राज्य, आम तौर पर तीन तरीकों का इस्तेमाल किया जाता है प्रतिक्रिया में गहराई से नेस्टेड वस्तुओं / चर को संशोधित करने Object.assign, अचल स्थिति-हेल्पर और cloneDeepसे Lodash

इसे प्राप्त करने के लिए अन्य कम लोकप्रिय तीसरे पक्ष के काम भी बहुत हैं, लेकिन इस उत्तर में, मैं इन तीन विकल्पों को कवर करूंगा। इसके अलावा, कुछ अतिरिक्त वेनिला जावास्क्रिप्ट विधियाँ मौजूद हैं, जैसे सरणी का प्रसार, (उदाहरण के लिए @ एमपीएन का जवाब देखें), लेकिन वे बहुत सहज, सभी राज्य हेरफेर स्थितियों को संभालने के लिए उपयोग करने में सक्षम और सक्षम नहीं हैं।

जैसा कि शीर्ष मतों के जवाब में असंख्य बार बताया गया था, जिनके लेखक राज्य के प्रत्यक्ष उत्परिवर्तन का प्रस्ताव रखते हैं: बस ऐसा मत करो । यह एक सर्वव्यापी रिएक्ट विरोधी पैटर्न है, जो अनिवार्य रूप से अवांछित परिणामों को जन्म देगा। सही तरीका सीखें।

आइए तीन व्यापक रूप से उपयोग किए जाने वाले तरीकों की तुलना करें।

इस राज्य वस्तु संरचना को देखते हुए:

state = {
    outer: {
        inner: 'initial value'
    }
}

innerराज्य के बाकी हिस्सों को प्रभावित किए बिना आंतरिक- क्षेत्र के मूल्य को अपडेट करने के लिए आप निम्न विधियों का उपयोग कर सकते हैं ।

1. वेनिला जावास्क्रिप्ट ऑब्जेक्ट

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()मूल 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 इसलिए आप केवल तभी काम करेंगे जब आपके पास आदिम प्रकार के मूल्यों को रखने वाले अंतरतम सदस्यों के साथ अपेक्षाकृत एक स्तर की गहरी राज्य संरचना हो।

यदि आपके पास गहरी वस्तुएं (दूसरा स्तर या अधिक) हैं, जिन्हें आपको अपडेट करना चाहिए, उपयोग न करें Object.assign। आप सीधे राज्य को म्यूट करने का जोखिम उठाते हैं।

2. लोधश का क्लोन

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>

लॉडश के क्लोनडिप का उपयोग करने का तरीका अधिक सरल है। यह एक गहरी क्लोनिंग करता है , इसलिए यह एक मजबूत विकल्प है, यदि आपके पास बहु-स्तरीय वस्तुओं या सरणियों के अंदर एक काफी जटिल स्थिति है। बस 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

यदि आप एक विशाल और / या जटिल स्थिति में हेरफेर करते हैं , तो लोदश cloneDeepएक बुद्धिमान विकल्प है।

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

... या, क्या आपको वास्तव में ऐसा करने की आवश्यकता है?

यदि आप प्रतिक्रिया की स्थिति में एक जटिल डेटा रखते हैं, तो शायद इसे संभालने के अन्य तरीकों के बारे में सोचने का यह अच्छा समय है। रिएक्ट घटकों में एक जटिल राज्य वस्तुओं को सही सेट करना एक सीधा ऑपरेशन नहीं है, और मैं दृढ़ता से विभिन्न दृष्टिकोणों के बारे में सोचने का सुझाव देता हूं।

सबसे अधिक संभावना है कि आप अपने जटिल डेटा को Redux स्टोर में रखना बेहतर मानते हैं, इसे वहां रीड्यूसर और / या सागा का उपयोग करके सेट करते हैं और चयनकर्ताओं का उपयोग करके इसे एक्सेस करते हैं।


Object.assignएक गहरी प्रति प्रदर्शन नहीं करता है। कहते हैं, यदि a = {c: {d: 1}}और b = Object.assign({}, a)फिर आप निष्पादित करते हैं b.c.d = 4, तो a.c.dउत्परिवर्तित होता है।
20

आप सही हैं, 1अंतरतम ऑब्जेक्ट ( a.c.d) का मान उत्परिवर्तित हो जाएगा। लेकिन अगर आप bइस तरह के पहले-स्तर के उत्तराधिकारी को फिर से सौंप देंगे b.c = {f: 1}, तो इसके समान भाग को aउत्परिवर्तित नहीं किया जाएगा (यह रुक जाएगा {d: 1})। वैसे भी अच्छी पकड़ है, मैं तुरंत जवाब अपडेट करूंगा।
न्यूरोट्रांसमीटर

आपने जो परिभाषित किया है वह वास्तव में ए shallow copyऔर ए नहीं है deep copy। इसका क्या shallow copyमतलब है, इसे भ्रमित करना आसान है। में shallow copy, a !== bलेकिन स्रोत ऑब्जेक्ट से प्रत्येक कुंजी के लिए a,a[key] === b[key]
Awol

हां, Object.assignउत्तर में स्पष्ट रूप से उथलेपन का उल्लेख किया गया है।
न्यूरोट्रांसमीटर

JSON.parse(JSON.stringify(object))गहरी क्लोन के लिए भी एक प्रकार है। प्रदर्शन तब और खराब होता है, cloneDeepहालांकि बाद में। मापक। net
Benchmark/

35

मुझे भी यही समस्या थी। यहाँ एक सरल उपाय है जो काम करता है!

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

11
@TranslucentCloud - यह सबसे निश्चित रूप से प्रत्यक्ष म्यूटेशन नहीं है। मूल सरणी को क्लोन किया गया, संशोधित किया गया और फिर क्लोन एरे का उपयोग करके राज्य को फिर से सेट किया गया।
vsync

@vsync हाँ, अब आपके द्वारा मूल उत्तर को संपादित करने के बाद यह एक उत्परिवर्तन नहीं है।
न्यूरोट्रांसमीटर

2
@TranslucentCloud - इससे पहले, मेरे संपादन का इससे कोई लेना-देना नहीं है, इस रवैये के लिए बहुत-बहुत धन्यवाद। @ जोनास ने यहाँ केवल अपने उत्तर में एक सरल गलती की है, {कोष्ठक के बजाय घुंघराले ब्रेसिज़ का उपयोग किया है जिसे मैंने हटा दिया है
vsync

31

सेटस्टेट पर प्रतिक्रिया दस्तावेज के अनुसार , Object.assignअन्य उत्तरों द्वारा सुझाए गए का उपयोग करना यहां आदर्श नहीं है। setStateअतुल्यकालिक व्यवहार की प्रकृति के कारण , इस तकनीक का उपयोग करने वाली बाद की कॉलें अवांछनीय परिणामों के कारण पिछले कॉल को ओवरराइड कर सकती हैं।

इसके बजाय, रिएक्ट डॉक्स updater फॉर्म का उपयोग करने की सलाह देते हैं, setStateजो पिछले राज्य पर संचालित होता है। ध्यान रखें कि किसी ऐरे या ऑब्जेक्ट को अपडेट करते समय आपको एक नया एरे या ऑब्जेक्ट वापस करना होगा क्योंकि रिएक्ट के लिए हमें राज्य की अपरिवर्तनीयता को संरक्षित करने की आवश्यकता होती है। ES6 वाक्यविन्यास के प्रसार ऑपरेटर का उपयोग उथले कॉपी करने के लिए, किसी सरणी के दिए गए इंडेक्स पर किसी ऑब्जेक्ट की संपत्ति को बनाने या अपडेट करने के लिए इस तरह दिखेगा:

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

2
यदि आप ES6 का उपयोग करते हैं तो यह उचित उत्तर है। यह इनलाइन है जो @ जोंस ने उत्तर दिया है। हालांकि स्पष्टीकरण के कारण यह बाहर खड़ा है।
सौरभ

हाँ यह कोड बिल्कुल ठीक काम कर रहा है और एक बहुत ही सरल ..
शोएब मिर्ज़ा

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
मार्टिंस

नहीं, यह नहीं होगा। आपकी त्रुटि आपके द्वारा किए जा रहे तरीके से सबसे अधिक संभावना है getInitialState
हेनरिक एंडर्सन

10
@HenrikAndersson कुछ आपके उदाहरण में सही नहीं लगता है। itemsकहीं भी परिभाषित नहीं है।
एडवर्ड डिसूजा

1
@ EdwardD'Souza तुम बिल्कुल सही हो! मेरा उत्तर यह दिखाना है कि इसे कैसे परिभाषित और उपयोग किया जाना चाहिए। जिस तरह से आस्कर्स कोड सेटअप होता है, वह उस चीज के लिए काम नहीं करता है, जिसके लिए वह / वह चाहेगा, जिसके लिए कीड ऑब्जेक्ट की आवश्यकता है।
हेनरिक एंडरसन

7
यह एक विशिष्ट रिएक्ट विरोधी पैटर्न है, आप itemराज्य के स्वयं के this.state.items[1]चर का संदर्भ दे रहे हैं । फिर आप संशोधित करते हैं item( item.name = 'newName'), और इस प्रकार सीधे राज्य को बदल देते हैं, जो अत्यधिक हतोत्साहित करता है। आपके उदाहरण में, कॉल करने की कोई आवश्यकता नहीं है this.setState({items: items}), क्योंकि राज्य पहले से ही सीधे रूप से उत्परिवर्तित है।
न्यूरोट्रांसमीटर 10

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या कुछ और था?
बुआ बुह

मैंने इसका परीक्षण किया लेकिन वह एक पंक्ति से गायब है। ऊपर items[1] = item;कहावत होनी चाहिए items = this.state.items;। खबरदार मेरी जावास्क्रिप्ट जंग खा रही है और मैं अपने घर के प्रोजेक्ट के लिए प्रतिक्रिया सीख रहा हूं, इसलिए मुझे इस बारे में कोई जानकारी नहीं है कि यह अच्छा है या बुरा :-)
ग्रेगोरी

4

यह वास्तव में सरल है।

पहले पूरे आइटम ऑब्जेक्ट को राज्य से खींच लें, आइटम ऑब्जेक्ट के भाग को वांछित के रूप में अपडेट किया, और सेटस्टेट के माध्यम से पूरे आइटम ऑब्जेक्ट को वापस राज्य में डाल दिया।

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
}

"Object.assign काम करेगा यदि आपके पास एक सबसे सरल एक स्तर की गहरी राज्य संरचना है जिसमें अंतरतम सदस्यों के पास आदिम प्रकार के मान हैं"।
न्यूरोट्रांसमीटर

3

जैसा कि उपरोक्त विकल्पों में से कोई भी मेरे लिए आदर्श नहीं था, मैंने मानचित्र का उपयोग करके समाप्त किया:

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सेट है।
न्यूरोट्रांसमीटर

क्या प्रदर्शन के लिए सरणी में मैप प्लस की तुलना भयानक नहीं है?
नटेशिया तवारेस

@NatassiaTavares .. क्या? हो सकता है कि आप भ्रमित हों fo ofया forEachमानचित्र सबसे तेज़ हो।
डीनो

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 से अनुकूलित

अद्यतन किए जाने वाले सरणी सदस्य एक अधिक नेस्टेड कॉम्प्लेक्स ऑब्जेक्ट है जो जटिलता के आधार पर उपयुक्त गहरी प्रतिलिपि विधि का उपयोग करता है।

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

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
यह मूल रूप से लगभग एक साल पहले पोस्ट किए गए इस अन्य उत्तर के समान है
23P

0

इवेंट का उपयोग उस handleChangeतत्व का पता लगाने के लिए करें जो बदल गया है और फिर इसे अपडेट करें। इसके लिए आपको इसे पहचानने और इसे अपडेट करने के लिए कुछ संपत्ति को बदलने की आवश्यकता हो सकती है।

फिडल देखें https://jsfiddle.net/69z2wepo/6164/


0

मैं फ़ंक्शन हैंडल परिवर्तन को स्थानांतरित करूंगा और एक सूचकांक पैरामीटर जोड़ूंगा

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

डायनामिक फॉर्म कंपोनेंट के लिए और इसे एक प्रोप के रूप में PopulateAtCheckboxes कंपोनेंट में पास करें। जैसा कि आप अपनी वस्तुओं पर लूप करते हैं, आप एक अतिरिक्त काउंटर (नीचे दिए गए कोड में अनुक्रमणिका) को शामिल कर सकते हैं ताकि नीचे दिखाए गए परिवर्तन को संभाल सकें।

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

कभी भी रिएक्ट की स्थिति को सीधे मत बदलिए।
न्यूरोट्रांसमीटर

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? यदि आप ऐसा करते हैं, तो राज्य को केवल एक आइटम के साथ नहीं छोड़ेंगे?
माइक्रोन

यह ऑब्जेक्ट के newItemsलिए एक नई प्रॉपर्टी को पेश करेगा state, और मौजूदा itemsप्रॉपर्टी को अपडेट नहीं करेगा ।
न्यूरोट्रांसमीटर

0

या यदि आपके पास डायनामिक रूप से जेनरेट की गई सूची है और आप इंडेक्स को नहीं जानते हैं, लेकिन केवल कुंजी या आईडी है:

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. सेट फंक्शन - यह फंक्शन कंपोनेंट इंडेक्स के आधार पर सरणी में डेटा डालता है।
<SubObjectForm setData={this.setSubObjectData}                                                            objectIndex={index}/>

यहां {Index} को उस स्थिति के आधार पर पास किया जा सकता है जहां इस SubObjectForm का उपयोग किया जाता है।

और 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 में, यह .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'});
रेम

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

कभी भी रिएक्ट की स्थिति को सीधे मत बदलिए।
न्यूरोट्रांसमीटर 10

-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");
   }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.