Redux में विशिष्ट सरणी आइटम के अंदर एकल मान को कैसे अपडेट करें


106

मेरे पास एक ऐसा मुद्दा है जहां राज्य का पुन: प्रतिपादन ui मुद्दों का कारण बनता है और केवल पृष्ठ पर पुन: प्रतिपादन की मात्रा को कम करने के लिए मेरे reducer के भीतर विशिष्ट मूल्य को अद्यतन करने का सुझाव दिया गया था।

यह मेरे राज्य का उदाहरण है

{
 name: "some name",
 subtitle: "some subtitle",
 contents: [
   {title: "some title", text: "some text"},
   {title: "some other title", text: "some other text"}
 ]
}

और मैं वर्तमान में इसे इस तरह से अपडेट कर रहा हूं

case 'SOME_ACTION':
   return { ...state, contents: action.payload }

action.payloadनए मूल्यों वाले एक पूरे सरणी में कहां है। लेकिन अब मुझे वास्तव में सामग्री सरणी में दूसरे आइटम के पाठ को अपडेट करने की आवश्यकता है, और ऐसा कुछ काम नहीं करता है

case 'SOME_ACTION':
   return { ...state, contents[1].text: action.payload }

action.payloadअब मुझे अपडेट के लिए एक पाठ की आवश्यकता कहां है

जवाबों:


58

आप रिएक्ट इम्युनिटी हेल्पर्स का उपयोग कर सकते हैं

import update from 'react-addons-update';

// ...    

case 'SOME_ACTION':
  return update(state, { 
    contents: { 
      1: {
        text: {$set: action.payload}
      }
    }
  });

हालांकि मुझे लगता है कि आप शायद इस तरह से कुछ और कर रहे होंगे?

case 'SOME_ACTION':
  return update(state, { 
    contents: { 
      [action.id]: {
        text: {$set: action.payload}
      }
    }
  });

वास्तव में, क्या मुझे किसी तरह अपने reducer के अंदर प्रतिक्रिया से इन सहायकों को शामिल करने की आवश्यकता है?
इलजा

8
हालांकि पैकेज को स्थानांतरित कर दिया गया है kolodny/immutability-helper, इसलिए अब यह है yarn add immutability-helperऔरimport update from 'immutability-helper';
SacWebDeveloper

मेरा मामला बिल्कुल वैसा ही है, लेकिन जब मैं एरे में तत्वों में से किसी एक में ऑब्जेक्ट को अपडेट करता हूं, तो अपडेटेड वैल्यू कंपोनेंट वेल में नहीं दिखता है। मैं सीधे अपने mapStateToProps में सामग्री का उपयोग करता हूं, क्या हमें इसे प्रतिबिंबित करने के लिए mapStateToProps में कुछ और उपयोग करना है?
सृष्टि गुप्ता

171

आप उपयोग कर सकते हैं map। यहाँ एक उदाहरण कार्यान्वयन है:

case 'SOME_ACTION':
   return { 
       ...state, 
       contents: state.contents.map(
           (content, i) => i === 1 ? {...content, text: action.payload}
                                   : content
       )
    }

यही कारण है कि मैं भी कर रहा हूँ, मुझे यकीन नहीं है कि यह एक अच्छा दृष्टिकोण है या नहीं क्योंकि नक्शा एक नया सरणी लौटाएगा। कोई विचार?
थियागो सी। एस। वेंचुरा

@Ventura हाँ, यह अच्छा है क्योंकि यह सरणी के लिए एक नया संदर्भ बना रहा है और एक सादे नए ऑब्जेक्ट को अपडेट करने का इरादा रखता है (और केवल एक ही), जो कि आप वास्तव में चाहते हैं
ivcandela

3
मुझे लगता है कि यह सबसे अच्छा अनुराग है
यीशु गोमेज़

12
मैं भी अक्सर ऐसा करता हूं, लेकिन यह आवश्यक सूचकांक को अपडेट करने के बजाय सभी तत्वों पर पुनरावृति करने के लिए अपेक्षाकृत महंगा कम्प्यूटेशनल लगता है।
बेन क्रेसी

सुंदर वाला ! मुझे यह पसंद है !
नैट बेन

21

आपको एक पंक्ति में सब कुछ करने की ज़रूरत नहीं है:

case 'SOME_ACTION':
  const newState = { ...state };
  newState.contents = 
    [
      newState.contents[0],
      {title: newState.contnets[1].title, text: action.payload}
    ];
  return newState

1
आप ठीक कह रहे हैं, मैंने जल्दी ठीक किया। btw, सरणी निश्चित लंबाई है? और क्या सूचकांक आप के रूप में अच्छी तरह से संशोधित करना चाहते हैं? वर्तमान दृष्टिकोण केवल छोटे सरणी और निश्चित सूचकांक के साथ व्यवहार्य है।
युया

2
अपने केस बॉडी को {} में लपेटें, क्योंकि कॉन्स्ट ब्लॉक ब्लॉक हो गया है।
बेनी पॉवर्स

15

पार्टी के लिए बहुत देर हो चुकी है लेकिन यहां एक सामान्य समाधान है जो हर सूचकांक मूल्य के साथ काम करता है।

  1. आप indexबदलना चाहते हैं और पुराने सरणी से नए सरणी को फैलाना चाहते हैं।

  2. मनचाहा डेटा जोड़ें।

  3. indexआप जिस सरणी के अंत में बदलना चाहते हैं, उससे नई सरणी बनाएं और फैलाएं

let index=1;// probabbly action.payload.id
case 'SOME_ACTION':
   return { 
       ...state, 
       contents: [
          ...state.contents.slice(0,index),
          {title: "some other title", text: "some other text"},
         ...state.contents.slice(index+1)
         ]
    }

अपडेट करें:

मैंने कोड को सरल बनाने के लिए एक छोटा मॉड्यूल बनाया है, इसलिए आपको बस एक फ़ंक्शन को कॉल करने की आवश्यकता है:

case 'SOME_ACTION':
   return {
       ...state,
       contents: insertIntoArray(state.contents,index, {title: "some title", text: "some text"})
    }

अधिक उदाहरणों के लिए, भंडार पर एक नज़र डालें

फ़ंक्शन हस्ताक्षर:

insertIntoArray(originalArray,insertionIndex,newData)

4

मेरा मानना ​​है कि जब आपको अपने Redux राज्य पर इस प्रकार के संचालन की आवश्यकता होती है तो प्रसार ऑपरेटर आपका मित्र होता है और यह प्रिंसिपल सभी बच्चों के लिए लागू होता है।

चलो बहाना है यह आपका राज्य है:

const state = {
    houses: {
        gryffindor: {
          points: 15
        },
        ravenclaw: {
          points: 18
        },
        hufflepuff: {
          points: 7
        },
        slytherin: {
          points: 5
        }
    }
}

और आप Ravenclaw में 3 अंक जोड़ना चाहते हैं

const key = "ravenclaw";
  return {
    ...state, // copy state
    houses: {
      ...state.houses, // copy houses
      [key]: {  // update one specific house (using Computed Property syntax)
        ...state.houses[key],  // copy that specific house's properties
        points: state.houses[key].points + 3   // update its `points` property
      }
    }
  }

स्प्रेड ऑपरेटर का उपयोग करके आप केवल नई स्थिति को अपडेट कर सकते हैं और बाकी सब कुछ बरकरार रख सकते हैं।

इस अद्भुत लेख से लिया गया उदाहरण , आप महान उदाहरणों के साथ लगभग हर संभव विकल्प पा सकते हैं।


3

मेरे मामले में मैंने कुछ ऐसा किया, जो लुइस के जवाब पर आधारित है:

...State object...
userInfo = {
name: '...',
...
}

...Reducer's code...
case CHANGED_INFO:
return {
  ...state,
  userInfo: {
    ...state.userInfo,
    // I'm sending the arguments like this: changeInfo({ id: e.target.id, value: e.target.value }) and use them as below in reducer!
    [action.data.id]: action.data.value,
  },
};

0

मैंने अपनी परियोजनाओं में से एक के लिए यह किया है:

const markdownSaveActionCreator = (newMarkdownLocation, newMarkdownToSave) => ({
  type: MARKDOWN_SAVE,
  saveLocation: newMarkdownLocation,
  savedMarkdownInLocation: newMarkdownToSave  
});

const markdownSaveReducer = (state = MARKDOWN_SAVED_ARRAY_DEFAULT, action) => {
  let objTemp = {
    saveLocation: action.saveLocation, 
    savedMarkdownInLocation: action.savedMarkdownInLocation
  };

  switch(action.type) {
    case MARKDOWN_SAVE:
      return( 
        state.map(i => {
          if (i.saveLocation === objTemp.saveLocation) {
            return Object.assign({}, i, objTemp);
          }
          return i;
        })
      );
    default:
      return state;
  }
};

0

मुझे डर है कि map()एक सरणी का उपयोग करना महंगा हो सकता है क्योंकि पूरे सरणी को पुनरावृत्त किया जाना है। इसके बजाय, मैं एक नया सरणी जोड़ती हूं जिसमें तीन भाग होते हैं:

  • सिर - संशोधित आइटम से पहले आइटम
  • संशोधित आइटम
  • पूंछ - संशोधित आइटम के बाद आइटम

यहां मैंने अपने कोड (NgRx, अभी तक machanism में अन्य Redux कार्यान्वयन के लिए समान है) का उदाहरण दिया है:

// toggle done property: true to false, or false to true

function (state, action) {
    const todos = state.todos;
    const todoIdx = todos.findIndex(t => t.id === action.id);

    const todoObj = todos[todoIdx];
    const newTodoObj = { ...todoObj, done: !todoObj.done };

    const head = todos.slice(0, todoIdx - 1);
    const tail = todos.slice(todoIdx + 1);
    const newTodos = [...head, newTodoObj, ...tail];
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.