প্রতিক্রিয়া: কীভাবে state.item আপডেট করতে হবে [1] স্টেট স্টেট ব্যবহার করে?


257

আমি এমন একটি অ্যাপ তৈরি করছি যেখানে ব্যবহারকারী নিজের ফর্মটি ডিজাইন করতে পারেন। যেমন ক্ষেত্রের নাম এবং কোন অন্যান্য কলামগুলি অন্তর্ভুক্ত করা উচিত তার বিশদ উল্লেখ করুন।

উপাদানটি এখানে একটি জেএসফিডাল হিসাবে উপলব্ধ ।

আমার প্রাথমিক অবস্থাটি দেখতে এমন দেখাচ্ছে:

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()পদ্ধতিতে এই আইটেমটির পরিবর্তনগুলি সনাক্ত করতে সক্ষম হওয়ার বিষয়ে চিন্তা না করেন তবে আপনি ===সরাসরি রাষ্ট্র সম্পাদনা করতে পারেন এবং উপাদানটিকে পুনরায় রেন্ডার করতে বাধ্য করতে পারেন - এটি কার্যকরভাবে @ লাইমলাইটের উত্তর হিসাবে একই, কারণ এটি কোনও বস্তুকে রাষ্ট্রের বাইরে টেনে এডিট করা।

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একটি অ্যারে তৈরি করেছি । ওপি একটি অবজেক্ট ব্যবহার করেছে। তবে ধারণাটি একই রকম the


আপনার টার্মিনাল / কনসোলে কী চলছে তা আপনি দেখতে পারেন:

 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
@ ট্রান্সলুসেন্টক্লাউড ওহ হ্যাঁ, সহায়ক পদ্ধতিগুলি অবশ্যই দুর্দান্ত, তবে আমি মনে করি প্রত্যেকের জানা উচিত হুডের নীচে কী চলছে :-)
এমপেন

আমাদের দ্বিতীয় এবং তিন ধাপের দরকার কেন? আমরা প্রথম পদক্ষেপের পরে ক্লোনটিকে সরাসরি কেন পরিবর্তন করতে পারি না?
এভমোরভ

1
@ এভমরভ কারণ এটি কোনও গভীর ক্লোন নয়। পদক্ষেপ 1 কেবল অ্যারের ক্লোনিং করছে, ভিতরে থাকা বস্তুগুলি নয়। অন্য কথায়, নতুন অ্যারের অভ্যন্তরের প্রতিটি বস্তু মেমরির বিদ্যমান অবজেক্টগুলিকে "পয়েন্ট" করে - একটিকে পরিবর্তিত করে অন্যটিকে পরিবর্তন করে (তারা এক এবং একই)। আরও দেখুন items[0] === clone[0]নীচে আমার টার্মিনাল উদাহরণে বিট। ট্রিপল =পরীক্ষা করে যদি বস্তুগুলি একই জিনিসকে বোঝায়।
এমপিএন

অ্যারেতে আইটেমের সূচক না জানলে আরও জটিল হয়ে ওঠে।
আয়ান ওয়ারবার্টন

পছন্দ করুন 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?
nkkollaw

49
items[1].role = e.target.valueমিউটেশন স্টেটকে কি সরাসরি ডাকা হচ্ছে না ?
অ্যান্টনি

28
আপনি রাষ্ট্রকে পরিবর্তন দিচ্ছেন, রাজ্যটিকে স্থির রাখার ধারণার বিরুদ্ধে এটি সম্পূর্ণরূপে প্রস্তাবিত প্রতিক্রিয়ার মতো। এটি আপনাকে একটি বড় অ্যাপ্লিকেশনটিতে প্রচুর ব্যথা দিতে পারে।
ncubica

8
@ মারভিনভিনকে আপনার উত্তরটি বলছে "সুতরাং সর্বোত্তম অনুশীলন হবে:" এর পরে "this.forUpdate () ব্যবহার করা হবে!" যা সেটস্টেট () দ্বারা ওভাররাইট করা যেতে পারে বলে প্রস্তাবিত নয়, দেখুন ফেসবুক . github.io/react/docs/react-component.html#state । এটি পরিবর্তনের পক্ষে সেরা এটি ভবিষ্যতের পাঠকদের কাছে বিভ্রান্তিকর নয়।
জেমস জেড।

7
কেবল এটি উল্লেখ করতে চেয়েছিলাম যে সম্পাদনার কারণে এখন অনেকগুলি মন্তব্য অপ্রাসঙ্গিক এবং সঠিক উপায়টি আসলে সঠিক উপায় is
হিজ

50

রিএ্যাক্টের রাজ্যে গভীরভাবে নেস্টেড অবজেক্ট / ভেরিয়েবলগুলি সংশোধন করতে সাধারণত তিনটি পদ্ধতি ব্যবহার করা হয়: ভ্যানিলা জাভাস্ক্রিপ্ট Object.assign, অপরিবর্তনীয়তা-সহায়ক এবং লোডাশcloneDeep থেকে ।

এটি অর্জনের জন্য অন্যান্য প্রচুর কম জনপ্রিয় তৃতীয় পক্ষের libs রয়েছে, তবে এই উত্তরে আমি এই তিনটি বিকল্পই কভার করব। এছাড়াও, কিছু অতিরিক্ত ভ্যানিলা জাভাস্ক্রিপ্ট পদ্ধতি রয়েছে যেমন অ্যারে ছড়িয়ে দেওয়ার মতো (উদাহরণস্বরূপ @ এমপেনের উত্তর দেখুন), তবে এগুলি খুব স্বজ্ঞাত, ব্যবহার করা সহজ এবং সমস্ত রাষ্ট্রের কারসাজির পরিস্থিতি পরিচালনা করতে সক্ষম নয়।

যেমন উত্তরগুলিতে শীর্ষে মতামত দেওয়া মন্তব্যে অসংখ্য সময় চিহ্নিত হয়েছিল, যার লেখকরা রাষ্ট্রের সরাসরি পরিবর্তনের প্রস্তাব দেয়: কেবল এটি করবেন না । এটি সর্বব্যাপী প্রতিক্রিয়া বিরোধী-প্যাটার্ন, যা অনিবার্যভাবে অযাচিত পরিণতির দিকে পরিচালিত করবে। সঠিক উপায় শিখুন।

আসুন তিনটি বহুল ব্যবহৃত পদ্ধতির তুলনা করি।

এই স্টেট অবজেক্ট স্ট্রাকচারটি দেওয়া হয়েছে:

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>

মনে রাখবেন, অবজেক্ট.সেসাইন একটি গভীর ক্লোনিং করবে না , যেহেতু এটি কেবল সম্পত্তি মানগুলি অনুলিপি করে এবং তাই এটি যা করে তাকে অগভীর অনুলিপি বলা হয় (মন্তব্য দেখুন)।

এটি কাজ করার জন্য, আমাদের কেবল প্রাথমিক প্রকারের বৈশিষ্ট্যগুলি ( 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, এটি একটি অত্যন্ত উন্নত সরঞ্জাম যা রাষ্ট্রের কারসাজির জন্য ব্যবহার করা যেতে পারে।

... বা, আপনার কি আসলেই এটি করা দরকার?

আপনি যদি প্রতিক্রিয়ার রাজ্যে কোনও জটিল ডেটা ধরে থাকেন তবে এটি পরিচালনা করার অন্যান্য উপায়গুলি নিয়ে ভাবার জন্য এটি বেশ ভাল সময়। সরাসরি প্রতিক্রিয়া উপাদানগুলিতে একটি জটিল রাষ্ট্রীয় অবজেক্টগুলি সেট করা সহজবোধ্য ক্রিয়াকলাপ নয় এবং আমি দৃ strongly়ভাবে বিভিন্ন পদ্ধতির বিষয়ে চিন্তাভাবনা করার পরামর্শ দিই।

সম্ভবত আপনার জটিল ডেটা রেডাক্স স্টোরে রেখে, হ্রাসকারী এবং / অথবা সাগাস ব্যবহার করে এটি সেট করা এবং নির্বাচকগুলি ব্যবহার করে এটি অ্যাক্সেস করা ভাল।


Object.assignএকটি গভীর অনুলিপি সম্পাদন করে না। বলুন, যদি a = {c: {d: 1}}এবং b = Object.assign({}, a), তারপর আপনি মৃত্যুদণ্ড কার্যকর করেন b.c.d = 4, তবে a.c.dমিউটেশন করা হয়।
আওল

আপনি ঠিক বলেছেন, 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যদিও লড্যাশ ।measurethat.net/Benchmarks/Show/2751/0/...
tylik

35

আমারও একই সমস্যা ছিল। এখানে একটি সহজ সমাধান যা কাজ করে!

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

11
@ ট্রান্সলুসেন্টক্লাউড - এটি অবশ্যই সরাসরি রূপান্তর নয়। আসল অ্যারে ক্লোন করা, সংশোধন করা হয়েছিল এবং তারপরে ক্লোনযুক্ত অ্যারে ব্যবহার করে আবার রাজ্য সেট করা হয়েছিল।
vsync

@vsync হ্যাঁ, এখন আপনি আসল উত্তরটি সম্পাদনা করার পরে এটি মোটেও কোনও রূপান্তর নয়।
নিউরোট্রান্সমিটার

2
@ ট্রান্সলুসেন্টক্লাউড - এছাড়াও এর আগে, আমার সম্পাদনাটির কিছুই করার নেই, মনোভাবের জন্য অনেক ধন্যবাদ। @ জোনাস এখানে কেবল তার উত্তরে একটি সাধারণ ভুল করেছিলেন, {আমি বন্ধনীর পরিবর্তে কোঁকড়া ধনুর্বন্ধনী ব্যবহার করেছি যা আমি প্রতিকার করেছি
vsync

31

সেটস্টেটে প্রতিক্রিয়াযুক্ত ডকুমেন্টেশন অনুসারে , Object.assignএখানে অন্যান্য উত্তরের পরামর্শ অনুসারে ব্যবহার করা আদর্শ নয়। setStateএর অ্যাসিঙ্ক্রোনাস আচরণের প্রকৃতির কারণে , এই কৌশলটি ব্যবহার করে পরবর্তী কলগুলি অনাকাঙ্ক্ষিত ফলাফলের ফলে পূর্ববর্তী কলগুলিকে ওভাররাইড করতে পারে।

পরিবর্তে, প্রতিক্রিয়া দস্তাবেজগুলি 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
@ হেনরিক অ্যান্ডারসন আপনার উদাহরণে ঠিক মনে হচ্ছে না। itemsকোথাও সংজ্ঞায়িত করা হয় না।
এডওয়ার্ড ডি সুজা

1
@ এডওয়ার্ড ডি'সুজা আপনি একেবারে সঠিক! আমার উত্তরটি এটি কীভাবে সংজ্ঞায়িত করা এবং ব্যবহার করা উচিত তা দেখানো। প্রশ্নকারীদের কোডটি যেভাবে সেটআপ করা হচ্ছে সে তার পক্ষে কাজ করে না, সে কারণেই কী-বোর্ডের প্রয়োজন হয়।
হেনরিক অ্যান্ডারসন

7
এটি একটি সাধারণ প্রতিক্রিয়া বিরোধী-প্যাটার্ন, আপনি itemরাজ্যের নিজস্ব this.state.items[1]পরিবর্তনশীলকে একটি রেফারেন্স নির্ধারণ করছেন । তারপরে আপনি item( item.name = 'newName') পরিবর্তন করুন এবং এভাবে সরাসরি রূপান্তরিত করুন যা অত্যন্ত নিরুৎসাহিত is আপনার উদাহরণে, এমনকি কল করার দরকার নেই this.setState({items: items}), কারণ ইতিমধ্যে রাজ্যটি সরাসরি রূপান্তরিত হয়েছে।
নিউরোট্রান্সমিটার

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
}

"অবজেক্ট.সেসাইন কাজ করবে যদি আপনার আঞ্চলিক সদস্যদের সাথে আদিম ধরণের মান ধারণ করে তুলনামূলকভাবে সহজ এক স্তর গভীর রাষ্ট্রীয় কাঠামো থাকে"।
নিউরোট্রান্সমিটার

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সেট করা আছে তা আমি দেখতে পাচ্ছি না ।
নিউরোট্রান্সমিটার

অভিনয়ের জন্য অ্যারেতে কোনও মানচিত্রের তুলনা কি ভয়াবহ নয়?
নাটাসিয়া টাভারেস

@ নাটাসিয়া টাভরেস .. কি? সম্ভবত আপনি বিভ্রান্তfo of বা forEachমানচিত্রটি দ্রুততম test
দেওানো

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#computes-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
এই করতে মূলত অভিন্ন এই অন্য উত্তর প্রায় পোস্ট এক বছর আগে
CertainPerformance

0

handleChangeযে উপাদানটি পরিবর্তিত হয়েছে তা বের করার জন্য ইভেন্টটি ব্যবহার করুন এবং তারপরে এটি আপডেট করুন। তার জন্য আপনাকে এটি চিহ্নিত করতে এবং আপডেট করার জন্য কিছু সম্পত্তি পরিবর্তন করতে হবে।

ফিডল https://jsfiddle.net/69z2wepo/6164/ দেখুন


0

আমি ফাংশনটির হ্যান্ডেল পরিবর্তনটি স্থানান্তর করব এবং একটি সূচক পরামিতি যুক্ত করব

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

ডায়নামিক ফর্ম উপাদানটিতে এবং পপুলেটএটিচেকবাক্সেস উপাদানটিকে প্রোপ হিসাবে পাস করুন। আপনি নিজের আইটেমগুলি লুপ করার সাথে সাথে হ্যান্ডেল পরিবর্তনের সাথে সাথে নীচে প্রদর্শিত হবে এমন একটি অতিরিক্ত কাউন্টার (নীচের কোডে সূচক বলা হয়) অন্তর্ভুক্ত করতে পারেন

{ 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? আপনি যদি তা করেন, তবে কি কেবল পরে কেবল একটি আইটেম দিয়ে রাজ্যটি ছেড়ে যাবে না?
micnil

এটি একটি নতুন সম্পত্তি পরিচয় করিয়ে দিতে হবে 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}/>

এই সাবঅবজেক্টফর্ম ব্যবহৃত হয় এমন অবস্থার উপর ভিত্তি করে এখানে {সূচক be পাশ করা যেতে পারে।

এবং সেটসুবজেক্টডেটা এমন কিছু হতে পারে।

 setSubObjectData: function(index, data){
      var arrayFromParentObject= <retrieve from props or state>;
      var objectInArray= arrayFromParentObject.array[index];
      arrayFromParentObject.array[index] = Object.assign(objectInArray, data);
 }

সাবঅবজেক্টফর্মে, এই.পড়গুলি.সেটটা ডেটা নীচে দেওয়া হিসাবে ডেটা পরিবর্তনে কল করা যেতে পারে।

<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'});
১ram:

0

@ জনিবুকাননের উত্তর পুরোপুরি কার্যকর হয় তবে কেবল অ্যারে স্টেট ভেরিয়েবলের জন্য। যদি রাষ্ট্রের ভেরিয়েবলটি কেবল একটি একক অভিধান হয় তবে এটি অনুসরণ করুন:

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

সরাসরি প্রতিক্রিয়ার রাজ্যকে কখনও রূপান্তরিত করবেন না।
নিউরোট্রান্সমিটার

-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.