Redux Reducer में स्टोर की प्रारंभिक स्थिति पढ़ें


85

Redux ऐप में प्रारंभिक स्थिति को दो तरीकों से सेट किया जा सकता है:

  • इसे दूसरे तर्क के रूप में पास करें createStore( डॉक्स लिंक )
  • इसे अपने पहले उप-तर्क के रूप में पास करें (उप) रिड्यूसर ( डॉक्स लिंक )

यदि आप अपने स्टोर में प्रारंभिक राज्य पास करते हैं, तो आप स्टोर से उस स्थिति को कैसे पढ़ते हैं और इसे अपने रीड्यूसर में पहला तर्क देते हैं?

जवाबों:


185

टी एल; डॉ

बिना combineReducers()या समान मैनुअल कोड के, initialStateहमेशा state = ...reducer पर जीतता है क्योंकि reducer को stateपारित किया गया है initialState और नहीं है undefined , इसलिए ES6 तर्क वाक्यविन्यास इस मामले में लागू नहीं होता है।

combineReducers()व्यवहार के साथ अधिक सूक्ष्म है। उन रिड्यूसर जिनके राज्य में निर्दिष्ट किया गया है, वे initialStateइसे प्राप्त करेंगे state। अन्य रिड्यूसर प्राप्त करेंगे undefined और इसके कारणstate = ... वे निर्दिष्ट किए गए डिफ़ॉल्ट तर्क पर वापस आ जाएंगे ।

सामान्य तौर पर, initialStateरिड्यूसर द्वारा निर्दिष्ट राज्य पर जीत होती है। यह Reducers को प्रारंभिक डेटा निर्दिष्ट करने देता है जो उन्हें डिफ़ॉल्ट तर्क के रूप में समझ में आता है , लेकिन मौजूदा डेटा (पूरी तरह या आंशिक रूप से) को लोड करने की अनुमति देता है जब आप स्टोर को कुछ लगातार भंडारण या सर्वर से हाइड्रेट कर रहे होते हैं।

पहले एक मामले पर विचार करें जहां आपके पास एक एकल reducer है।
कहो तुम उपयोग नहीं करते combineReducers()

तब आपका रिड्यूसर इस तरह दिख सकता है:

function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT': return state + 1;
  case 'DECREMENT': return state - 1;
  default: return state;
  }
}

अब मान लेते हैं कि आप इसके साथ एक स्टोर बनाते हैं।

import { createStore } from 'redux';
let store = createStore(counter);
console.log(store.getState()); // 0

प्रारंभिक अवस्था शून्य है। क्यों? क्योंकि दूसरा तर्क createStoreथा undefined। यह stateपहली बार आपके reducer को दिया गया है। जब Redux इनिशियलाइज़ करता है तो यह राज्य को भरने के लिए एक "डमी" एक्शन भेजता है। तो अपने counterreducer के stateबराबर बुलाया गया था undefinedयह ठीक यही स्थिति है कि डिफ़ॉल्ट तर्क को "सक्रिय" करता है। इसलिए, stateअब 0डिफ़ॉल्ट stateमान ( state = 0) के अनुसार है। यह राज्य ( 0) वापस कर दिया जाएगा।

आइए एक अलग परिदृश्य पर विचार करें:

import { createStore } from 'redux';
let store = createStore(counter, 42);
console.log(store.getState()); // 42

यह समय क्यों है 42, और नहीं है 0? क्योंकि दूसरे तर्क के createStoreसाथ बुलाया गया था 42। यह तर्क stateडमी एक्शन के साथ-साथ आपके रिड्यूसर के पास जाता है। इस बार, stateअपरिभाषित नहीं है (यह 42!), इसलिए ES6 डिफ़ॉल्ट तर्क वाक्यविन्यास का कोई प्रभाव नहीं है। stateहै 42, और 42कम करने से दिया जाता है।


अब एक मामले पर विचार करते हैं जहां आप उपयोग करते हैं combineReducers()
आपके पास दो रिड्यूसर हैं:

function a(state = 'lol', action) {
  return state;
}

function b(state = 'wat', action) {
  return state;
}

इस combineReducers({ a, b })तरह से उत्पन्न reducer इस तरह दिखता है:

// const combined = combineReducers({ a, b })
function combined(state = {}, action) {
  return {
    a: a(state.a, action),
    b: b(state.b, action)
  };
}

अगर हम createStoreबिना कॉल करते हैं initialState, तो यह इनिशियलाइज़ करने वाला stateहै {}। इसलिए, state.aऔर जब तक यह कॉल और रिड्यूसर state.bकरेगा तब undefinedतक होगा । दोनों और रिड्यूसर अपने तर्क के रूप में प्राप्त करेंगे , और यदि वे डिफ़ॉल्ट मान निर्दिष्ट करते हैं, तो उन्हें वापस कर दिया जाएगा। यह कैसे संयुक्त reducer पहले आह्वान पर एक राज्य वस्तु देता है ।ababundefined statestate{ a: 'lol', b: 'wat' }

import { createStore } from 'redux';
let store = createStore(combined);
console.log(store.getState()); // { a: 'lol', b: 'wat' }

आइए एक अलग परिदृश्य पर विचार करें:

import { createStore } from 'redux';
let store = createStore(combined, { a: 'horse' });
console.log(store.getState()); // { a: 'horse', b: 'wat' }

अब मैंने initialStateतर्क के रूप में निर्दिष्ट किया createStore()। संयुक्त रिड्यूसर से लौटा राज्य प्रारंभिक स्थिति को जोड़ता है जिसे मैंने aरिड्यूसर के लिए 'wat'डिफ़ॉल्ट तर्क के साथ निर्दिष्ट किया था कि bरिड्यूसर ने खुद को चुना।

आइए याद करें कि संयुक्त रेड्यूसर क्या करता है:

// const combined = combineReducers({ a, b })
function combined(state = {}, action) {
  return {
    a: a(state.a, action),
    b: b(state.b, action)
  };
}

इस मामले में, stateनिर्दिष्ट किया गया था इसलिए यह वापस नहीं आया {}। यह aफ़ील्ड के बराबर एक ऑब्जेक्ट था 'horse', लेकिन bफ़ील्ड के बिना । यही कारण है कि aरिड्यूसर ने 'horse'इसके रूप में प्राप्त किया stateऔर खुशी से इसे वापस कर दिया, लेकिन bरिड्यूसर ने undefinedइसके रूप में प्राप्त किया stateऔर इस तरह डिफ़ॉल्ट के अपने विचार को वापस कर दियाstate (हमारे उदाहरण में, 'wat')। इस तरह हम { a: 'horse', b: 'wat' }बदले में मिलते हैं।


इसे जमा करने के लिए, यदि आप Redux सम्मेलनों से चिपके रहते हैं और जब वे तर्क के undefinedरूप में कहे जाते हैं, तो Reducers से प्रारंभिक स्थिति लौटाते हैं state(इसे लागू करने का सबसे आसान तरीका stateES6 डिफ़ॉल्ट तर्क मान निर्दिष्ट करना है ), आप करने जा रहे हैं संयुक्त reducers के लिए एक अच्छा उपयोगी व्यवहार। वे initialStateउस createStore()फ़ंक्शन में संबंधित मान को पसंद करेंगे जो आप फ़ंक्शन में पास करते हैं, लेकिन यदि आपने कोई पास नहीं किया है, या यदि संबंधित फ़ील्ड सेट नहीं है, stateतो Reducer द्वारा निर्दिष्ट डिफ़ॉल्ट तर्क इसके बजाय चुना जाता है।यह दृष्टिकोण अच्छी तरह से काम करता है क्योंकि यह मौजूदा डेटा के आरंभ और जलयोजन दोनों को प्रदान करता है, लेकिन व्यक्तिगत रीड्यूसर अपने राज्य को रीसेट कर देता है यदि उनका डेटा संरक्षित नहीं था। बेशक आप इस पैटर्न को पुनरावर्ती रूप से लागू कर सकते हैं, जैसा कि आप combineReducers()कई स्तरों पर उपयोग कर सकते हैं , या यहां तक ​​कि रिड्यूसर को मैन्युअल रूप से रिड्यूसर को कॉल करके और उन्हें राज्य के पेड़ का प्रासंगिक हिस्सा दे सकते हैं।


3
महान विस्तार के लिए धन्यवाद - वास्तव में मैं क्या देख रहा था। यहां आपके एपीआई का लचीलापन शानदार है। जो हिस्सा मैं नहीं देख रहा था, वह यह है कि "बच्चा" reducer समझ जाएगा कि प्रारंभिक अवस्था का कौन सा टुकड़ा उसमें इस्तेमाल की गई कुंजी के आधार पर है combineReducers। फिर से बहुत-बहुत धन्यवाद।
कैंटरा

इस उत्तर के लिए धन्यवाद, Dan। यदि मैं आपके उत्तर को सही ढंग से पचा रहा हूं, तो आप कह रहे हैं कि एक reducer के एक्शन प्रकार के माध्यम से प्रारंभिक स्थिति सेट करना सबसे अच्छा होगा? उदाहरण के लिए, GET_INITIAL_STATE और उस एक्शन टाइप में एक प्रेषण कॉल करें, आइए हम कहते हैं, एक CompDidMount कॉलबैक?
कोन एंटोनकोस

@ConAntonakos नहीं, मेरा यह मतलब नहीं है। मेरा मतलब है कि प्रारंभिक राज्य को चुनना जहां आप रिड्यूसर को परिभाषित करते हैं, जैसे function counter(state = 0, action)या function visibleIds(state = [], action)
दान अब्रामोव

1
@DanAbramov: createstore () में दूसरे तर्क का उपयोग करते हुए, मैं एसपीए पृष्ठ पर राज्य को फिर से कैसे बदल सकता हूं, इसके लिए अंतिम मानों के बजाय redux में अंतिम संग्रहीत स्थिति के साथ पुनः लोड करें। मुझे लगता है मैं पूछ रहा हूं कि मैं पूरे स्टोर को कैसे कैश कर सकता हूं और इसे फिर से लोड करने के लिए संरक्षित कर सकता हूं और इसे क्रिएस्टोरोर फ़ंक्शन में पास कर सकता हूं।
जसन

1
@ साजन: लोकलस्टोरेज का इस्तेमाल करें। egghead.io/lessons/…
Dan Abramov

5

संक्षेप में: यह रिड्यूस है जो प्रारंभिक राज्य को रिड्यूसर से गुजरता है, आपको कुछ भी करने की आवश्यकता नहीं है।

जब आप कॉल createStore(reducer, [initialState])करते हैं तो आप Redux को बता देते हैं कि पहली क्रिया में आने पर Reducer को प्रारंभिक अवस्था में पारित किया जा सकता है।

दूसरा विकल्प जिसका आप उल्लेख करते हैं, केवल उसी स्थिति में लागू होता है जब आपने स्टोर बनाते समय एक प्रारंभिक स्थिति पारित नहीं की थी। अर्थात

function todoApp(state = initialState, action)

यदि Redux द्वारा पारित कोई राज्य नहीं था, तो राज्य केवल आरंभिक होगा


1
जवाब देने के लिए धन्यवाद - reducer रचना के मामले में, यदि आपने स्टोर में प्रारंभिक राज्य लागू किया है, तो आप बच्चे / उप रेड्यूसर को कैसे बताते हैं कि प्रारंभिक राज्य के पेड़ का कौन सा हिस्सा इसका मालिक है? उदाहरण के लिए, यदि आपके पास एक है menuState, तो मैं state.menuस्टोर से मूल्य पढ़ने के लिए मेनू reducer को कैसे बताऊं और इसका प्रारंभिक स्थिति के रूप में उपयोग करें?
कैंटर

धन्यवाद, लेकिन मेरा प्रश्न स्पष्ट नहीं होना चाहिए। मैं यह देखने के लिए इंतजार करूंगा कि क्या संपादन से पहले दूसरे जवाब देते हैं।
कैंटरा

1

आप स्टोर से उस स्थिति को कैसे पढ़ते हैं और इसे अपने रीड्यूसर में पहला तर्क देते हैं?

CombReducers () आपके लिए काम करते हैं। इसे लिखने का पहला तरीका वास्तव में उपयोगी नहीं है:

const rootReducer = combineReducers({ todos, users })

लेकिन अन्य एक, जो समकक्ष है अधिक स्पष्ट है:

function rootReducer(state, action) {
   todos: todos(state.todos, action),
   users: users(state.users, action)
}

0

मुझे आशा है कि यह आपके अनुरोध का जवाब देता है (जिसे मैंने intialState को पारित करते समय और उस स्थिति को वापस करते हुए reducers को इनिशियलाइज़ करना समझा था)

हम इसे कैसे करते हैं (चेतावनी: टाइपस्क्रिप्ट कोड से कॉपी किया गया)।

इसका सार if(!state)मुख्यरेडर (फ़ैक्टरी) फ़ंक्शन में परीक्षण है

function getInitialState(): MainState {

     return {
         prop1:                 'value1',
         prop1:                 'value2',
         ...        
     }
}



const reducer = combineReducers(
    {
        main:     mainReducer( getInitialState() ),
        ...
    }
)



const mainReducer = ( initialState: MainState ): Reducer => {

    return ( state: MainState, action: Action ): MainState => {

        if ( !state ) {
            return initialState
        }

        console.log( 'Main reducer action: ', action ) 

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