क्या Redux का उपयोग करके किसी आइटम को हटाने का यह सही तरीका है?


116

मुझे पता है कि मैं इनपुट को म्यूट करने वाला नहीं हूं और इसे म्यूट करने के लिए ऑब्जेक्ट को क्लोन करना चाहिए। मैं एक redux स्टार्टर परियोजना में इस्तेमाल किए गए सम्मेलन का पालन कर रहा था जो इस्तेमाल किया गया था:

ADD_ITEM: (state, action) => ({
  ...state,
  items: [...state.items, action.payload.value],
  lastUpdated: action.payload.date
})

एक आइटम जोड़ने के लिए - मुझे सरणी में आइटम को जोड़ने के लिए प्रसार का उपयोग मिलता है।

हटाने के लिए मैंने इस्तेमाल किया:

DELETE_ITEM: (state, action) => ({
  ...state,
  items: [...state.items.splice(0, action.payload), ...state.items.splice(1)],
  lastUpdated: Date.now() 
})

लेकिन यह इनपुट स्टेट ऑब्जेक्ट को म्यूट कर रहा है - क्या यह मना है भले ही मैं एक नई वस्तु वापस कर रहा हूं?


1
त्वरित प्रश्न। स्प्लिस आपके द्वारा हटाए गए आइटमों को वापस करता है। क्या आपका इरादा ऐसा है? यदि नहीं, तो आपको स्लाइस का उपयोग करना चाहिए, जो बिना किसी उत्परिवर्तन कानून का पालन करता है।
m0meni

अच्छी तरह से इस उदाहरण में मैं सरणी के दो वर्गों को एक नए सरणी में एक साथ जोड़ रहा हूं - जिस आइटम के साथ मैं बाईं ओर हटाना चाहता था। स्लाइस भी हटाए गए आइटम को सही लौटाता है? केवल यह मूल सरणी को म्यूट किए बिना ऐसा करता है ताकि बेहतर दृष्टिकोण हो?
CWright

@ AR7 आपके सुझाव के अनुसार: items: [...state.items.slice(0, action.payload.value), ...state.items.slice(action.payload.value + 1 )]स्लाइस के बजाय अब स्लाइस का उपयोग करें ताकि इनपुट को म्यूट न करें - क्या यह जाने का तरीका है या अधिक संक्षिप्त तरीका है?
CWright

जवाबों:


207

नहीं। कभी भी अपने राज्य को म्यूट न करें।

हालांकि आप एक नई वस्तु लौटा रहे हैं, फिर भी आप पुरानी वस्तु को प्रदूषित कर रहे हैं, जिसे आप कभी नहीं करना चाहते हैं। यह पुराने और नए राज्य के बीच तुलना करते समय समस्याग्रस्त बनाता है। उदाहरण के लिए shouldComponentUpdateजिसमें हुड के नीचे प्रतिक्रिया-रेडक्स का उपयोग होता है। यह समय यात्रा को असंभव भी बनाता है (यानी पूर्ववत करें और फिर से करें)।

इसके बजाय, अपरिवर्तनीय विधियों का उपयोग करें। हमेशा उपयोग करें Array#sliceऔर कभी नहींArray#splice

मैं आपके कोड से मानता हूं कि action.payloadआइटम को हटाए जाने का सूचकांक है। एक बेहतर तरीका इस प्रकार होगा:

items: [
    ...state.items.slice(0, action.payload),
    ...state.items.slice(action.payload + 1)
],

अगर हम एरे में अंतिम तत्व के साथ काम कर रहे हैं तो यह काम नहीं करता ...है, दूसरे स्टेटमेंट में भी आपके राज्य की सामग्री दोगुनी हो जाएगी
थेनॉर

4
कृपया सिद्ध करें कि jsfiddle / codepen उदाहरण के साथ। स्निपेट arr.slice(arr.length)को हमेशा एक खाली सरणी का उत्पादन करना चाहिए चाहे कोई भी सामग्री हो arr
डेविड एल वॉल्श

1
@ david-l-walsh भ्रम के लिए खेद है, मैंने इस उदाहरण का परीक्षण करते समय एक टाइपो या कुछ बनाया होगा। यह अद्भुत काम करता है। मेरा एकमात्र प्रश्न है: ...दूसरे भाग में प्रसार ऑपरेटर की आवश्यकता क्यों है -...state.items.slice(action.payload + 1)
थानेर

5
Array#sliceएक सरणी देता है। एक ही सरणी में दो स्लाइस को संयोजित करने के लिए, मैंने प्रसार ऑपरेटर का उपयोग किया है। इसके बिना, आपके पास सरणियों की एक सरणी होगी।
डेविड एल वॉल्श

4
यह समझ आता है। स्पष्ट करने के लिए बहुत बहुत धन्यवाद (और पहले भ्रम की स्थिति के लिए खेद है)।
थेनर 12

149

आप मूल राज्य को म्यूट किए बिना सरणी से किसी विशिष्ट तत्व को निकालने के लिए सरणी फ़िल्टर विधि का उपयोग कर सकते हैं।

return state.filter(element => element !== action.payload);

आपके कोड के संदर्भ में, यह कुछ इस तरह दिखाई देगा:

DELETE_ITEM: (state, action) => ({
  ...state,
  items: state.items.filter(item => item !== action.payload),
  lastUpdated: Date.now() 
})

1
क्या फिल्टर एक नई सरणी का उत्पादन करता है?
चेनोप

6
@chenop हाँ, Array.filter विधि एक नई सरणी देता है। developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Steph M

4
ध्यान दें कि यदि डुप्लिकेट हैं, तो यह सभी को हटा देगा। फिल्टर का उपयोग करने के लिए एक विशिष्ट सूचकांक दूर करने के लिए, आप उपयोग कर सकते हैं जैसेarr.filter((val, i) => i !== action.payload )
erich2k8

21

ES6 Array.prototype.filterविधि मापदंड से मेल खाने वाली वस्तुओं के साथ एक नया सरणी देता है। इसलिए, मूल प्रश्न के संदर्भ में, यह होगा:

DELETE_ITEM: (state, action) => ({
  ...state,
  items: state.items.filter(item => action.payload !== item),
  lastUpdated: Date.now() 
})

.filter()ES2015 विधि नहीं है, लेकिन पूर्व संस्करण ES5 में जोड़ा गया है।
मॉर्क्रो

7

वस्तुओं के साथ सरणी के लिए अपरिवर्तनीय "DELETED" reducer की एक और भिन्नता:

const index = state.map(item => item.name).indexOf(action.name);
const stateTemp = [
  ...state.slice(0, index),
  ...state.slice(index + 1)
];
return stateTemp;

0

स्वर्णिम नियम यह है कि हम एक उत्परिवर्तित अवस्था को नहीं लौटाते हैं, बल्कि एक नए राज्य को बनाते हैं। अपनी कार्रवाई के प्रकार के आधार पर, आपको अपने राज्य के पेड़ को विभिन्न रूपों में अपडेट करने की आवश्यकता हो सकती है जब यह reducer को हिट करता है।

इस परिदृश्य में हम एक आइटम को एक राज्य संपत्ति से निकालने का प्रयास कर रहे हैं।

यह हमें Redux के अपरिवर्तनीय अद्यतन (या डेटा संशोधन) पैटर्न की अवधारणा में लाता है। अपरिवर्तनीयता महत्वपूर्ण है क्योंकि हम कभी भी राज्य के पेड़ में एक मूल्य को सीधे बदलना नहीं चाहते हैं, बल्कि हमेशा एक प्रति बनाते हैं और पुराने मूल्य के आधार पर एक नया मान लौटाते हैं।

यहां एक नेस्टेड ऑब्जेक्ट को हटाने का एक उदाहरण दिया गया है:

// ducks/outfits (Parent)

// types
export const NAME = `@outfitsData`;
export const REMOVE_FILTER = `${NAME}/REMOVE_FILTER`;

// initialization
const initialState = {
  isInitiallyLoaded: false,
  outfits: ['Outfit.1', 'Outfit.2'],
  filters: {
    brand: [],
    colour: [],
  },
  error: '',
};

// action creators
export function removeFilter({ field, index }) {
  return {
    type: REMOVE_FILTER,
    field,
    index,
  };
}

export default function reducer(state = initialState, action = {}) {
  sswitch (action.type) {  
  case REMOVE_FILTER:
  return {
    ...state,
    filters: {
    ...state.filters,
       [action.field]: [...state.filters[action.field]]
       .filter((x, index) => index !== action.index)
    },
  };
  default:
     return state;
  }
}

इसे बेहतर ढंग से समझने के लिए, इस लेख को देखें।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.