क्या मैं reducer में एक कार्रवाई भेज सकता हूँ?


196

क्या किसी reducer में ही कार्रवाई को भेजना संभव है? मेरे पास एक प्रगतिपट्टी और एक ऑडियो तत्व है। ऑडियो तत्व में समय अद्यतन होने पर प्रगतिबार को अद्यतन करना लक्ष्य है। लेकिन मुझे नहीं पता कि ऑनटाइमअपडेट इवेंटहैंडलर को कहां रखा जाए, या प्रोग्रेसबार को अपडेट करने के लिए ऑनटाइमअपडेट के कॉलबैक में एक्शन कैसे भेजा जाए। यहाँ मेरा कोड है:

//reducer

const initialState = {
    audioElement: new AudioElement('test.mp3'),
    progress: 0.0
}

initialState.audioElement.audio.ontimeupdate = () => {
    console.log('progress', initialState.audioElement.currentTime/initialState.audioElement.duration);
    //how to dispatch 'SET_PROGRESS_VALUE' now?
};


const audio = (state=initialState, action) => {
    switch(action.type){
        case 'SET_PROGRESS_VALUE':
            return Object.assign({}, state, {progress: action.progress});
        default: return state;
    }

}

export default audio;

क्या है AudioElement? ऐसा लगता है कि राज्य में कुछ नहीं होना चाहिए।
ebuat3989

यह एक ऑडियो ऑब्जेक्ट को पकड़े हुए ES6 प्लेन क्लास (कोई प्रतिक्रिया नहीं) है। यदि यह राज्य में नहीं होगा, तो मैं कैसे खेल / नियंत्रण, लंघन आदि को नियंत्रित करूंगा?
klanm

2
आप redux गाथा
Kyeotic

जवाबों:


150

एक reducer के भीतर एक कार्रवाई का प्रेषण एक विरोधी पैटर्न है । आपका रिड्यूसर साइड इफेक्ट्स के बिना होना चाहिए, बस एक्शन पेलोड को पचाने और एक नया राज्य ऑब्जेक्ट वापस करना चाहिए। Reducer के भीतर श्रोताओं और प्रेषण क्रियाओं को जोड़ने से जंजीर क्रियाएं और अन्य दुष्प्रभाव हो सकते हैं।

आपकी आरंभिक AudioElementकक्षा की तरह लगता है और घटना श्रोता राज्य के बजाय एक घटक के भीतर हैं। ईवेंट श्रोता के भीतर आप एक एक्शन भेज सकते हैं, जो progressराज्य में अपडेट होगा ।

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

class MyAudioPlayer extends React.Component {
  constructor(props) {
    super(props);

    this.player = new AudioElement('test.mp3');

    this.player.audio.ontimeupdate = this.updateProgress;
  }

  updateProgress () {
    // Dispatch action to reducer with updated progress.
    // You might want to actually send the current time and do the
    // calculation from within the reducer.
    this.props.updateProgressAction();
  }

  render () {
    // Render the audio player controls, progress bar, whatever else
    return <p>Progress: {this.props.progress}</p>;
  }
}

class MyContainer extends React.Component {
   render() {
     return <MyAudioPlayer updateProgress={this.props.updateProgress} />
   }
}

function mapStateToProps (state) { return {}; }

return connect(mapStateToProps, {
  updateProgressAction
})(MyContainer);

ध्यान दें कि updateProgressActionस्वचालित रूप से लिपटे हुए हैं dispatchइसलिए आपको सीधे प्रेषण को कॉल करने की आवश्यकता नहीं है।


स्पष्टीकरण के लिए बहुत बहुत धन्यवाद! लेकिन मुझे अभी भी पता नहीं है कि डिस्पैचर का उपयोग कैसे किया जाता है। मैंने हमेशा प्रतिक्रिया-रिडक्स से कनेक्ट विधि का उपयोग किया। लेकिन मुझे नहीं पता कि इसे अपडेटप्रोग्रेड पद्धति में कैसे कॉल किया जाए। या डिस्पैचर पाने का एक और तरीका है। शायद सहारा के साथ? धन्यवाद
klanm

कोई दिक्कत नहीं है। आप MyAudioPlayerपैरेंट कंटेनर से उस घटक पर कार्रवाई कर सकते हैं जो इसके connectसाथ एड है react-redux। यहां देखें कि कैसे mapDispatchToPropsकरें: github.com/reactjs/react-redux/blob/master/docs/…
ebuat3989

6
updateProgressActionआपके उदाहरण में प्रतीक कहाँ परिभाषित किया गया है?
चार्ल्स प्रकाश दासारी

2
यदि आप एक reducer के भीतर एक कार्रवाई को भेजने के लिए नहीं माना जाता है, तो redux-thunk Redux के नियमों को तोड़ रहा है?
एरिक वीनर

2
@EricWiener मेरा मानना redux-thunkहै कि एक एक्शन किसी अन्य एक्शन से भेजा जा रहा है , न कि रिड्यूसर । stackoverflow.com/questions/35411423/…
sallf

160

आपके reducer समाप्त होने से पहले एक और प्रेषण शुरू करना एक विरोधी पैटर्न है , क्योंकि आपके reducer की शुरुआत में आपको जो राज्य प्राप्त हुआ है वह वर्तमान एप्लिकेशन स्थिति नहीं होगी जब आपका reducer समाप्त होता है। लेकिन एक reducer के भीतर से एक और प्रेषण का समय निर्धारण एक विरोधी पैटर्न नहीं है । वास्तव में, यह कि एल्म भाषा क्या करती है, और जैसा कि आप जानते हैं कि Redux एल्म वास्तुकला को जावास्क्रिप्ट में लाने का एक प्रयास है।

यहां एक मिडलवेयर है जो asyncDispatchआपके सभी कार्यों में संपत्ति जोड़ देगा । जब आपका रिड्यूसर समाप्त हो गया है और नया एप्लिकेशन स्टेट लौटाया है, तो आप इसे जो भी कार्रवाई देंगे , asyncDispatchउसके store.dispatchसाथ ट्रिगर होगा ।

// This middleware will just add the property "async dispatch"
// to actions with the "async" propperty set to true
const asyncDispatchMiddleware = store => next => action => {
  let syncActivityFinished = false;
  let actionQueue = [];

  function flushQueue() {
    actionQueue.forEach(a => store.dispatch(a)); // flush queue
    actionQueue = [];
  }

  function asyncDispatch(asyncAction) {
    actionQueue = actionQueue.concat([asyncAction]);

    if (syncActivityFinished) {
      flushQueue();
    }
  }

  const actionWithAsyncDispatch =
    Object.assign({}, action, { asyncDispatch });

  const res = next(actionWithAsyncDispatch);

  syncActivityFinished = true;
  flushQueue();

  return res;
};

अब आपका reducer ऐसा कर सकता है:

function reducer(state, action) {
  switch (action.type) {
    case "fetch-start":
      fetch('wwww.example.com')
        .then(r => r.json())
        .then(r => action.asyncDispatch({ type: "fetch-response", value: r }))
      return state;

    case "fetch-response":
      return Object.assign({}, state, { whatever: action.value });;
  }
}

7
मार्सेलो, आपकी ब्लॉग पोस्ट यहाँ आपके दृष्टिकोण की परिस्थितियों का वर्णन करते हुए एक बढ़िया काम करती है, इसलिए मैं इसे यहाँ लिंक कर रहा हूँ: lazamar.github.io/dispatching-from-inside-of-reducers
संजय क्लेटन

3
यह वही था जो मुझे चाहिए था, जैसा कि मिडलवेयर को छोड़कर-ब्रेक है dispatchजो कार्रवाई को वापस करना चाहिए। मैंने अंतिम पंक्तियों को बदल दिया: const res = next(actionWithAsyncDispatch); syncActivityFinished = true; flushQueue(); return res;और इसने बहुत काम किया।
झेनरॉक

1
यदि आप एक reducer के भीतर एक कार्रवाई को भेजने के लिए नहीं माना जाता है, तो redux-thunk Redux के नियमों को तोड़ रहा है?
एरिक वीनर

जब आप वेबसोकेट प्रतिक्रियाओं को संभालने की कोशिश करते हैं तो यह कैसे काम करता है? यह मेरा reducer निर्यात डिफ़ॉल्ट (राज्य, क्रिया) => {const m2 = [... State.messages, action.payload] वापस Object.assign ({}, राज्य, {संदेश: m2,})} और I STILL इस त्रुटि को प्राप्त करें "प्रेषण के बीच राज्य उत्परिवर्तन का पता चला था"
क्वांटम्पोटेटो

12

आप Redux-saga जैसी लाइब्रेरी का उपयोग करने का प्रयास कर सकते हैं । यह async फ़ंक्शंस को अनुक्रमित करने, कार्यों को आग लगाने, देरी का उपयोग करने और बहुत कुछ करने के लिए बहुत साफ तरीके से अनुमति देता है । यह बहुत शक्तिशाली है!


1
क्या आप यह निर्दिष्ट कर सकते हैं कि Redux-saga के साथ 'reducer के अंदर एक और प्रेषण शेड्यूलिंग' कैसे प्राप्त करें?
XPD

1
@XPD क्या आप कुछ और समझा सकते हैं जो आप पूरा करना चाहते हैं? यदि आप किसी अन्य एक्शन को भेजने के लिए रिड्यूसर एक्शन का उपयोग करने की कोशिश कर रहे हैं, तो आप रिड्यूक्स-सागा के समान कुछ नहीं कर पाएंगे।
chandlervdw

1
उदाहरण के लिए, मान लें कि आपके पास एक आइटम स्टोर है जहां आपने आइटम का एक भाग लोड किया है। आइटम आलसी लोड कर रहे हैं। मान लें कि एक आइटम में एक सप्लायर है। आपूर्तिकर्ताओं ने भी आलसी को लोड किया। तो इस मामले में ऐसा कोई आइटम हो सकता है जिसका आपूर्तिकर्ता लोड नहीं किया गया है। किसी आइटम रिड्यूसर में, अगर हमें किसी ऐसे आइटम के बारे में जानकारी प्राप्त करने की आवश्यकता होती है, जिसे अभी तक लोड नहीं किया गया है, तो हमें रिड्यूसर के माध्यम से फिर से सर्वर से डेटा लोड करना होगा। उस स्थिति में redux-saga एक reducer के अंदर कैसे मदद करता है?
XPD

1
ठीक है, मान लें कि supplierजब उपयोगकर्ता itemविवरण पृष्ठ पर जाने का प्रयास करता है , तो आप जानकारी के लिए इस अनुरोध को बंद कर देना चाहते थे । आपका componentDidMount()कहना है कि एक समारोह में आग लग जाएगी जो एक कार्रवाई को भेजती है, कहते हैं FETCH_SUPPLIER। रिड्यूसर के भीतर, आप loading: trueस्पिनर को दिखाने के लिए कुछ पर टिक कर सकते हैं जबकि अनुरोध किया जाता है। redux-sagaउस कार्रवाई के लिए सुनो, और जवाब में, वास्तविक अनुरोध बंद आग। जनरेटर के कार्यों का उपयोग, यह तब प्रतिक्रिया की प्रतीक्षा कर सकता है और इसे डंप कर सकता है FETCH_SUPPLIER_SUCCEEDED। Reducer फिर आपूर्तिकर्ता जानकारी के साथ स्टोर को अपडेट करता है।
chandlervdw 14

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