टाइमआउट के साथ Redux एक्शन कैसे भेजें?


889

मेरे पास एक कार्रवाई है जो मेरे आवेदन की अधिसूचना स्थिति को अपडेट करती है। आमतौर पर, यह सूचना किसी प्रकार की त्रुटि या सूचना होगी। मुझे फिर 5 सेकंड के बाद एक और कार्रवाई करने की आवश्यकता है जो अधिसूचना राज्य को प्रारंभिक एक पर लौटाएगी, इसलिए कोई अधिसूचना नहीं। इसके पीछे मुख्य कारण कार्यक्षमता प्रदान करना है जहां सूचनाएं 5 सेकंड के बाद स्वचालित रूप से गायब हो जाती हैं।

मुझे setTimeoutदूसरी कार्रवाई का उपयोग करने और वापस करने का कोई सौभाग्य नहीं था और यह नहीं पाया जा सकता है कि यह ऑनलाइन कैसे किया जाता है। इसलिए किसी भी सलाह का स्वागत है।


30
redux-sagaयदि आप थ्रक्स से बेहतर कुछ चाहते हैं, तो मेरे आधारित उत्तर की जांच करना न भूलें । देर से जवाब तो आप इसे प्रदर्शित करने से पहले एक लंबे समय स्क्रॉल करने के लिए है :) इसका मतलब यह नहीं है कि यह पढ़ने लायक नहीं है। यहाँ एक शॉर्टकट है: stackoverflow.com/a/38574266/82609
सेबेस्टियन लॉर्बर

5
जब भी आप सेटटाइमआउट करते हैं तो कंपोनेंट में क्लियर टाइमआउट का उपयोग करके टाइमर को हटाना न भूलें। जीवन चक्र विधि
हेमाद्रि दसारी

2
Redux-saga शांत है, लेकिन उन्हें जनरेटर कार्यों से टाइप किए गए प्रतिक्रियाओं के लिए समर्थन नहीं लगता है। यदि आप प्रतिक्रिया के साथ टाइपस्क्रिप्ट का उपयोग कर रहे हैं तो कोई बात नहीं।
क्रिश्चियन रामिरेज़

जवाबों:


2615

एक पुस्तकालय सोच के जाल में न पड़ें कि कैसे सब कुछ करना चाहिए । यदि आप जावास्क्रिप्ट में टाइमआउट के साथ कुछ करना चाहते हैं, तो आपको उपयोग करने की आवश्यकता है setTimeout। कोई कारण नहीं है कि Redux क्रियाएं किसी भी भिन्न क्यों न हों।

Redux अतुल्यकालिक सामान से निपटने के कुछ वैकल्पिक तरीके प्रदान करता है , लेकिन आपको केवल उनका उपयोग करना चाहिए जब आपको एहसास हो कि आप बहुत अधिक कोड दोहरा रहे हैं। जब तक आपको यह समस्या नहीं होती है, तब तक जो भाषा प्रदान करती है उसका उपयोग करें और सरलतम समाधान के लिए जाएं।

Async कोड इनलाइन लिखना

यह अब तक का सबसे सरल तरीका है। और यहाँ Redux के लिए कुछ खास नहीं है।

store.dispatch({ type: 'SHOW_NOTIFICATION', text: 'You logged in.' })
setTimeout(() => {
  store.dispatch({ type: 'HIDE_NOTIFICATION' })
}, 5000)

इसी तरह, एक जुड़े घटक के अंदर से:

this.props.dispatch({ type: 'SHOW_NOTIFICATION', text: 'You logged in.' })
setTimeout(() => {
  this.props.dispatch({ type: 'HIDE_NOTIFICATION' })
}, 5000)

अंतर केवल इतना है कि एक जुड़े घटक में आमतौर पर आपके पास स्टोर तक ही पहुंच नहीं होती है, लेकिन dispatch()या तो या विशिष्ट एक्शन क्रिएटरों को प्रॉप्स के रूप में इंजेक्ट किया जाता है। हालाँकि इससे हमें कोई फर्क नहीं पड़ता है।

यदि आप विभिन्न घटकों से समान क्रियाओं को भेजते समय टाइपोस बनाना पसंद नहीं करते हैं, तो आप एक्शन ऑब्जेक्ट्स को इनलाइन भेजने के बजाय एक्शन क्रिएटर्स को निकालना चाह सकते हैं:

// actions.js
export function showNotification(text) {
  return { type: 'SHOW_NOTIFICATION', text }
}
export function hideNotification() {
  return { type: 'HIDE_NOTIFICATION' }
}

// component.js
import { showNotification, hideNotification } from '../actions'

this.props.dispatch(showNotification('You just logged in.'))
setTimeout(() => {
  this.props.dispatch(hideNotification())
}, 5000)

या, यदि आपने पहले उन्हें साथ में बांधा है connect():

this.props.showNotification('You just logged in.')
setTimeout(() => {
  this.props.hideNotification()
}, 5000)

अभी तक हमने किसी मिडलवेयर या अन्य उन्नत अवधारणा का उपयोग नहीं किया है।

Async एक्शन क्रिएटर को निकालना

ऊपर का दृष्टिकोण सरल मामलों में ठीक काम करता है लेकिन आप पा सकते हैं कि इसमें कुछ समस्याएं हैं:

  • यह आपको इस तर्क की नकल करने के लिए मजबूर करता है, कहीं भी आप एक अधिसूचना दिखाना चाहते हैं।
  • यदि आपके पास दो सूचनाएं तेज़ी से दिखाई देती हैं, तो सूचनाओं की कोई आईडी नहीं है, इसलिए आपके पास दौड़ की स्थिति होगी। जब पहला टाइमआउट खत्म हो जाता है, तो यह HIDE_NOTIFICATIONटाइमआउट के बाद गलती से दूसरी अधिसूचना को जल्द ही छिपा देगा ।

इन समस्याओं को हल करने के लिए, आपको एक फ़ंक्शन निकालने की आवश्यकता होगी जो टाइमआउट तर्क को केंद्रीकृत करता है और उन दो कार्यों को भेजता है। यह इस तरह लग सकता है:

// actions.js
function showNotification(id, text) {
  return { type: 'SHOW_NOTIFICATION', id, text }
}
function hideNotification(id) {
  return { type: 'HIDE_NOTIFICATION', id }
}

let nextNotificationId = 0
export function showNotificationWithTimeout(dispatch, text) {
  // Assigning IDs to notifications lets reducer ignore HIDE_NOTIFICATION
  // for the notification that is not currently visible.
  // Alternatively, we could store the timeout ID and call
  // clearTimeout(), but we’d still want to do it in a single place.
  const id = nextNotificationId++
  dispatch(showNotification(id, text))

  setTimeout(() => {
    dispatch(hideNotification(id))
  }, 5000)
}

अब घटक showNotificationWithTimeoutइस तर्क को दोहराए बिना या विभिन्न सूचनाओं के साथ दौड़ की स्थिति होने पर उपयोग कर सकते हैं :

// component.js
showNotificationWithTimeout(this.props.dispatch, 'You just logged in.')

// otherComponent.js
showNotificationWithTimeout(this.props.dispatch, 'You just logged out.')    

पहले तर्क के रूप में क्यों showNotificationWithTimeout()स्वीकार करता dispatchहै? क्योंकि इसे स्टोर में कार्रवाई भेजने की आवश्यकता है। आम तौर पर एक घटक की पहुंच होती है dispatchलेकिन चूंकि हम डिस्पैचिंग पर नियंत्रण रखना चाहते हैं, इसलिए हमें इसे डिस्पैचिंग पर नियंत्रण देने की आवश्यकता है।

यदि आपके पास कुछ मॉड्यूल से निर्यात किया गया एक सिंगलटन स्टोर था, तो आप इसे dispatchसीधे आयात कर सकते हैं और इसके बजाय सीधे उस पर:

// store.js
export default createStore(reducer)

// actions.js
import store from './store'

// ...

let nextNotificationId = 0
export function showNotificationWithTimeout(text) {
  const id = nextNotificationId++
  store.dispatch(showNotification(id, text))

  setTimeout(() => {
    store.dispatch(hideNotification(id))
  }, 5000)
}

// component.js
showNotificationWithTimeout('You just logged in.')

// otherComponent.js
showNotificationWithTimeout('You just logged out.')    

यह सरल दिखता है, लेकिन हम इस दृष्टिकोण की अनुशंसा नहीं करते हैं । इसका मुख्य कारण हम इसे नापसंद करते हैं क्योंकि यह स्टोर को एक सिंगलटन होने के लिए मजबूर करता है । इससे सर्वर रेंडरिंग को लागू करना बहुत कठिन हो जाता है । सर्वर पर, आप चाहते हैं कि प्रत्येक अनुरोध का अपना स्टोर हो, ताकि विभिन्न उपयोगकर्ताओं को अलग-अलग प्रीलोडेड डेटा मिले।

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

इसलिए जब आप तकनीकी रूप से एक मॉड्यूल से एक सिंगलटन स्टोर का निर्यात कर सकते हैं, तो हम इसे हतोत्साहित करते हैं। ऐसा तब तक न करें जब तक आपको यकीन न हो कि आपका ऐप कभी सर्वर रेंडरिंग नहीं करेगा।

पिछले संस्करण पर वापस जाना:

// actions.js

// ...

let nextNotificationId = 0
export function showNotificationWithTimeout(dispatch, text) {
  const id = nextNotificationId++
  dispatch(showNotification(id, text))

  setTimeout(() => {
    dispatch(hideNotification(id))
  }, 5000)
}

// component.js
showNotificationWithTimeout(this.props.dispatch, 'You just logged in.')

// otherComponent.js
showNotificationWithTimeout(this.props.dispatch, 'You just logged out.')    

यह तर्क के दोहराव के साथ समस्याओं को हल करता है और हमें दौड़ की स्थिति से बचाता है।

थन मिडलवेयर

सरल ऐप के लिए, दृष्टिकोण को पर्याप्त होना चाहिए। यदि आप इससे खुश हैं तो मिडलवेयर की चिंता न करें।

हालाँकि, बड़े ऐप्स में, आपको इसके आस-पास कुछ असुविधाएँ हो सकती हैं।

उदाहरण के लिए, यह दुर्भाग्यपूर्ण है कि हमें dispatchचारों ओर से गुजरना होगा । इससे कंटेनर और प्रस्तुति घटकों को अलग करना मुश्किल हो जाता है क्योंकि कोई भी घटक जो Redux क्रियाओं को ऊपर के तरीके से अतुल्यकालिक रूप से भेजता dispatchहै, उसे एक प्रस्ताव के रूप में स्वीकार करना पड़ता है ताकि वह इसे और आगे पारित कर सके। आप एक्शन क्रिएटर्स को अभी नहीं बांध connect()सकते क्योंकि showNotificationWithTimeout()वास्तव में एक्शन क्रिएटर नहीं है। यह एक Redux कार्रवाई वापस नहीं करता है।

इसके अलावा, यह याद रखना अजीब हो सकता है कि कौन से फ़ंक्शंस सिंक्रोनस एक्शन क्रिएटर हैं जैसे showNotification()और जो एसिंक्रोनस हेल्पर्स जैसे हैं showNotificationWithTimeout()। आपको उन्हें अलग तरह से इस्तेमाल करना होगा और सावधान रहना होगा कि वे एक-दूसरे के साथ गलती न करें।

यह सहायक कार्य प्रदान dispatchकरने के इस पैटर्न को "वैध" करने का एक तरीका खोजने के लिए प्रेरणा थी , और Redux को पूरी तरह से विभिन्न कार्यों के बजाय सामान्य क्रिया रचनाकारों के विशेष मामले के रूप में इस तरह के अतुल्यकालिक कार्रवाई रचनाकारों को "देखने" में मदद करता है।

यदि आप अभी भी हमारे साथ हैं और आप भी अपने ऐप में एक समस्या के रूप में पहचानते हैं, तो आप Redux Thunk मिडलवेयर का उपयोग करने के लिए स्वागत करते हैं ।

वास्तव में कार्यों में होने वाली विशेष प्रकार की क्रियाओं को पहचानने के लिए Redux Thunk Redux सिखाता है:

import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'

const store = createStore(
  reducer,
  applyMiddleware(thunk)
)

// It still recognizes plain object actions
store.dispatch({ type: 'INCREMENT' })

// But with thunk middleware, it also recognizes functions
store.dispatch(function (dispatch) {
  // ... which themselves may dispatch many times
  dispatch({ type: 'INCREMENT' })
  dispatch({ type: 'INCREMENT' })
  dispatch({ type: 'INCREMENT' })

  setTimeout(() => {
    // ... even asynchronously!
    dispatch({ type: 'DECREMENT' })
  }, 1000)
})

जब यह मिडलवेयर सक्षम होता है, यदि आप किसी फंक्शन को भेजते हैं , तो Redux Thunk मिडलवेयर इसे dispatchएक तर्क के रूप में देगा । यह इस तरह की कार्रवाइयों को "निगल" भी करेगा ताकि अजीब फ़ंक्शन तर्क प्राप्त करने वाले अपने reducers के बारे में चिंता न करें। आपके रिड्यूसर केवल सादे ऑब्जेक्ट एक्शन प्राप्त करेंगे - या तो सीधे उत्सर्जित होते हैं, या फ़ंक्शंस द्वारा उत्सर्जित होते हैं जैसा कि हमने अभी वर्णन किया है।

यह बहुत उपयोगी नहीं दिखता है, यह करता है? इस विशेष स्थिति में नहीं। हालांकि यह हमें showNotificationWithTimeout()एक नियमित Redux एक्शन निर्माता के रूप में घोषित करता है:

// actions.js
function showNotification(id, text) {
  return { type: 'SHOW_NOTIFICATION', id, text }
}
function hideNotification(id) {
  return { type: 'HIDE_NOTIFICATION', id }
}

let nextNotificationId = 0
export function showNotificationWithTimeout(text) {
  return function (dispatch) {
    const id = nextNotificationId++
    dispatch(showNotification(id, text))

    setTimeout(() => {
      dispatch(hideNotification(id))
    }, 5000)
  }
}

ध्यान दें कि फ़ंक्शन हम पिछले अनुभाग में लिखे गए के समान लगभग समान है। हालाँकि यह dispatchपहले तर्क के रूप में स्वीकार नहीं करता है। इसके बजाय यह एक फ़ंक्शन देता है dispatchजो पहले तर्क के रूप में स्वीकार करता है।

हम इसे अपने घटक में कैसे उपयोग करेंगे? निश्चित रूप से, हम इसे लिख सकते हैं:

// component.js
showNotificationWithTimeout('You just logged in.')(this.props.dispatch)

हम आंतरिक फ़ंक्शन को प्राप्त करने के लिए async क्रिया निर्माता को कॉल कर रहे हैं जो बस चाहता है dispatch, और फिर हम पास होते हैं dispatch

हालाँकि यह मूल संस्करण की तुलना में अधिक अजीब है! हम भी उस रास्ते पर क्यों गए?

इससे पहले जो मैंने आपको बताया था। यदि Redux Thunk मिडलवेयर सक्षम है, तो किसी भी समय आप किसी एक्शन ऑब्जेक्ट के बजाय फ़ंक्शन को भेजने का प्रयास करते हैं, मिडिलवेयर उस फ़ंक्शन dispatchको पहले तर्क के रूप में विधि के साथ कॉल करेगा

तो हम इसके बजाय यह कर सकते हैं:

// component.js
this.props.dispatch(showNotificationWithTimeout('You just logged in.'))

अंत में, एक अतुल्यकालिक कार्रवाई (वास्तव में, क्रियाओं की एक श्रृंखला) को भेजने से घटक के लिए एक एकल कार्रवाई को भेजने से अलग नहीं दिखता है। यह अच्छा है क्योंकि घटकों को परवाह नहीं करनी चाहिए कि क्या कुछ तुल्यकालिक या अतुल्यकालिक रूप से होता है। हमने अभी-अभी अमूर्त किया है।

ध्यान दें कि जब से हमने "सिखाया" Redux को ऐसे "विशेष" एक्शन क्रिएटर्स को पहचानने के लिए (हम उन्हें थंक एक्शन क्रिएटर कहते हैं ), अब हम उन्हें किसी भी स्थान पर उपयोग कर सकते हैं जहाँ हम नियमित एक्शन क्रिएटर्स का उपयोग करेंगे। उदाहरण के लिए, हम उनका उपयोग कर सकते हैं connect():

// actions.js

function showNotification(id, text) {
  return { type: 'SHOW_NOTIFICATION', id, text }
}
function hideNotification(id) {
  return { type: 'HIDE_NOTIFICATION', id }
}

let nextNotificationId = 0
export function showNotificationWithTimeout(text) {
  return function (dispatch) {
    const id = nextNotificationId++
    dispatch(showNotification(id, text))

    setTimeout(() => {
      dispatch(hideNotification(id))
    }, 5000)
  }
}

// component.js

import { connect } from 'react-redux'

// ...

this.props.showNotificationWithTimeout('You just logged in.')

// ...

export default connect(
  mapStateToProps,
  { showNotificationWithTimeout }
)(MyComponent)

थ्रक्स में स्टेट पढ़ना

आमतौर पर आपके रिड्यूसर में अगले राज्य का निर्धारण करने के लिए व्यावसायिक तर्क होते हैं। हालाँकि, क्रियाओं को भेजने के बाद reducers केवल किक करते हैं। क्या होगा यदि आपके पास एक थंक एक्शन क्रिएटर में साइड इफेक्ट (जैसे एपीआई कॉल करना) है, और आप इसे किसी शर्त के तहत रोकना चाहते हैं?

थंक मिडलवेयर का उपयोग किए बिना, आप बस घटक के अंदर यह जांच करेंगे:

// component.js
if (this.props.areNotificationsEnabled) {
  showNotificationWithTimeout(this.props.dispatch, 'You just logged in.')
}

हालाँकि, एक्शन क्रिएटर को निकालने का बिंदु कई घटकों में इस दोहराव वाले तर्क को केंद्रीकृत करना था। सौभाग्य से, Redux Thunk आपको Redux स्टोर की वर्तमान स्थिति पढ़ने का एक तरीका प्रदान करता है । इसके अलावा dispatch, यह getStateउस फ़ंक्शन के दूसरे तर्क के रूप में भी जाता है जिसे आप अपने थंक एक्शन क्रिएटर से वापस करते हैं। यह थंक स्टोर की वर्तमान स्थिति को पढ़ने देता है।

let nextNotificationId = 0
export function showNotificationWithTimeout(text) {
  return function (dispatch, getState) {
    // Unlike in a regular action creator, we can exit early in a thunk
    // Redux doesn’t care about its return value (or lack of it)
    if (!getState().areNotificationsEnabled) {
      return
    }

    const id = nextNotificationId++
    dispatch(showNotification(id, text))

    setTimeout(() => {
      dispatch(hideNotification(id))
    }, 5000)
  }
}

इस पैटर्न का दुरुपयोग न करें। कैश डेटा उपलब्ध होने पर एपीआई कॉल से बाहर निकलना अच्छा है, लेकिन यह आपके व्यापार तर्क को बनाने के लिए बहुत अच्छी नींव नहीं है। यदि आप getState()केवल अलग-अलग कार्यों को सशर्त रूप से भेजने के लिए उपयोग करते हैं, तो इसके बजाय व्यापार तर्क को रिड्यूसर में डालने पर विचार करें।

अगला कदम

अब आपके पास एक बुनियादी अंतर्ज्ञान है कि थ्रॉक्स कैसे काम करते हैं, Redux async उदाहरण देखें जो उनका उपयोग करता है।

आपको ऐसे कई उदाहरण मिल सकते हैं जिनमें थ्रो वापस आने का वादा करता है। यह आवश्यक नहीं है, लेकिन बहुत सुविधाजनक हो सकता है। Redux को इस बात से कोई फ़र्क नहीं पड़ता है कि आप एक थंक से क्या लौटाते हैं, लेकिन यह आपको इसकी वापसी का मूल्य देता है dispatch()। यही कारण है कि आप एक थंक से एक वादा वापस कर सकते हैं और कॉल करके इसे पूरा करने की प्रतीक्षा कर सकते हैं dispatch(someThunkReturningPromise()).then(...)

आप जटिल थंक एक्शन क्रिएटर्स को कई छोटे थंक एक्शन क्रिएटर्स में विभाजित कर सकते हैं। dispatchThunks द्वारा प्रदान की विधि ही Thunks स्वीकार कर सकते हैं, तो आप पैटर्न रिकर्सिवली आवेदन कर सकते हैं। फिर, यह वादा के साथ सबसे अच्छा काम करता है क्योंकि आप उस के शीर्ष पर अतुल्यकालिक नियंत्रण प्रवाह को लागू कर सकते हैं।

कुछ ऐप्स के लिए, आप अपने आप को एक ऐसी स्थिति में पा सकते हैं, जहाँ आपके अतुल्यकालिक नियंत्रण प्रवाह की आवश्यकताएं भी थ्रक्स के साथ व्यक्त की जानी बहुत जटिल हैं। उदाहरण के लिए, असफल अनुरोधों को पुन: प्राप्त करना, टोकन के साथ सौंदर्यीकरण प्रवाह, या इस तरह से लिखे जाने पर एक कदम-दर-चरण ऑनबोर्डिंग बहुत क्रिया और त्रुटि-प्रवण हो सकता है। इस मामले में, आप अधिक उन्नत अतुल्यकालिक नियंत्रण प्रवाह समाधान जैसे Redux Saga या Redux Loop को देखना चाह सकते हैं । उनका मूल्यांकन करें, अपनी आवश्यकताओं के लिए प्रासंगिक उदाहरणों की तुलना करें, और आपको जो सबसे ज्यादा पसंद है उसे चुनें।

अंत में, अगर आप उनके लिए वास्तविक जरूरत नहीं है तो कुछ भी (थ्रॉक्स सहित) का उपयोग न करें। याद रखें कि आवश्यकताओं के आधार पर, आपका समाधान उतना ही सरल लग सकता है

store.dispatch({ type: 'SHOW_NOTIFICATION', text: 'You logged in.' })
setTimeout(() => {
  store.dispatch({ type: 'HIDE_NOTIFICATION' })
}, 5000)

जब तक आप यह नहीं जानते कि आप ऐसा क्यों कर रहे हैं, तो इसे न करें।


27
Async क्रियाएँ एक सामान्य समस्या के लिए एक सरल और सुरुचिपूर्ण समाधान की तरह लगती हैं। मिडलवेयर की आवश्यकता के बिना रिडक्स में बेक किए गए उनके लिए समर्थन क्यों नहीं है? यह जवाब इतना अधिक संक्षिप्त हो सकता है।
फिल मंडर

83
@PhilMander क्योंकि कई वैकल्पिक पैटर्न हैं जैसे कि github.com/raisemarketplace/redux-loop या github.com/yelouafi/redux-saga जो सिर्फ (यदि अधिक नहीं) तो सुरुचिपूर्ण हैं। Redux एक निम्न-स्तरीय उपकरण है। आप अपनी पसंद का सुपरसेट बना सकते हैं और उसे अलग से वितरित कर सकते हैं।
दान अब्रामोव

16
क्या आप इसे समझा सकते हैं: * व्यापार तर्क को रिड्यूसर में डालने पर विचार करें *, क्या इसका मतलब है कि मुझे एक कार्रवाई भेजनी चाहिए, और फिर रिड्यूसर में यह निर्धारित करना चाहिए कि आगे की कार्रवाई मेरे राज्य के आधार पर क्या होगी? मेरा प्रश्न यह है कि क्या मैं अपने रेड्यूसर में सीधे अन्य कार्यों को भेजूँ, और यदि नहीं तो मैं उन्हें कहाँ से भेजूँ?
मेंढकवास

25
यह वाक्य केवल सिंक्रोनस केस पर लागू होता है। उदाहरण के लिए यदि आप लिखते हैं तो if (cond) dispatch({ type: 'A' }) else dispatch({ type: 'B' })शायद आपको बस dispatch({ type: 'C', something: cond })और action.somethingवर्तमान स्थिति के आधार पर रिड्यूसर में कार्रवाई को अनदेखा करना चाहिए ।
दान अब्रामोव

29
@DanAbramov आपको मेरा उत्थान सिर्फ इस लिए मिला "जब तक आपको यह समस्या नहीं है, तब तक जो भाषा प्रस्तुत करती है उसका उपयोग करें और सरलतम समाधान के लिए जाएं।" इसके बाद ही मुझे एहसास हुआ कि इसे किसने लिखा है!
मैट लेसी

188

Redux-saga का उपयोग करना

जैसा कि डैन अब्रामोव ने कहा, यदि आप अपने async कोड पर अधिक उन्नत नियंत्रण चाहते हैं, तो आप redux-saga पर एक नज़र डाल सकते हैं ।

यह उत्तर एक सरल उदाहरण है, यदि आप इस बारे में बेहतर स्पष्टीकरण चाहते हैं कि Redux-saga आपके आवेदन के लिए उपयोगी क्यों हो सकता है, तो इस अन्य उत्तर की जाँच करें

सामान्य विचार यह है कि Redux-saga एक ES6 जनरेटर दुभाषिया प्रदान करता है जो आपको आसानी से सिंक्रोनस कोड की तरह दिखने वाले Async कोड लिखने की अनुमति देता है (यही कारण है कि आप Redux-saga में लूप करते समय अक्सर अनंत पाएंगे)। किसी तरह, Redux-saga सीधे जावास्क्रिप्ट के अंदर अपनी भाषा का निर्माण कर रहा है। Redux-saga को पहली बार में सीखना थोड़ा मुश्किल हो सकता है, क्योंकि आपको जनरेटर की बुनियादी समझ की आवश्यकता होती है, लेकिन Redux-saga द्वारा प्रस्तुत भाषा को भी समझें।

मैं यहाँ बताने की कोशिश करूँगा कि मैंने जिस अधिसूचना प्रणाली को बनाया है, वह Redux-saga के ऊपर है। यह उदाहरण वर्तमान में उत्पादन में चलता है।

उन्नत अधिसूचना प्रणाली विनिर्देश

  • आप प्रदर्शित होने के लिए अधिसूचना का अनुरोध कर सकते हैं
  • आप छिपाने के लिए एक अधिसूचना का अनुरोध कर सकते हैं
  • एक सूचना को 4 सेकंड से अधिक प्रदर्शित नहीं किया जाना चाहिए
  • एक ही समय में कई सूचनाएं प्रदर्शित की जा सकती हैं
  • एक ही समय में 3 से अधिक सूचनाएं प्रदर्शित नहीं की जा सकती हैं
  • यदि एक अधिसूचना का अनुरोध किया जाता है, जबकि पहले से ही 3 प्रदर्शित सूचनाएं हैं, तो इसे कतार / स्थगित करें।

परिणाम

मेरे प्रोडक्शन ऐप Stample.co का स्क्रीनशॉट

टोस्ट

कोड

यहाँ मैंने नोटिफिकेशन को नाम दिया है toastलेकिन यह नामकरण विवरण है।

function* toastSaga() {

    // Some config constants
    const MaxToasts = 3;
    const ToastDisplayTime = 4000;


    // Local generator state: you can put this state in Redux store
    // if it's really important to you, in my case it's not really
    let pendingToasts = []; // A queue of toasts waiting to be displayed
    let activeToasts = []; // Toasts currently displayed


    // Trigger the display of a toast for 4 seconds
    function* displayToast(toast) {
        if ( activeToasts.length >= MaxToasts ) {
            throw new Error("can't display more than " + MaxToasts + " at the same time");
        }
        activeToasts = [...activeToasts,toast]; // Add to active toasts
        yield put(events.toastDisplayed(toast)); // Display the toast (put means dispatch)
        yield call(delay,ToastDisplayTime); // Wait 4 seconds
        yield put(events.toastHidden(toast)); // Hide the toast
        activeToasts = _.without(activeToasts,toast); // Remove from active toasts
    }

    // Everytime we receive a toast display request, we put that request in the queue
    function* toastRequestsWatcher() {
        while ( true ) {
            // Take means the saga will block until TOAST_DISPLAY_REQUESTED action is dispatched
            const event = yield take(Names.TOAST_DISPLAY_REQUESTED);
            const newToast = event.data.toastData;
            pendingToasts = [...pendingToasts,newToast];
        }
    }


    // We try to read the queued toasts periodically and display a toast if it's a good time to do so...
    function* toastScheduler() {
        while ( true ) {
            const canDisplayToast = activeToasts.length < MaxToasts && pendingToasts.length > 0;
            if ( canDisplayToast ) {
                // We display the first pending toast of the queue
                const [firstToast,...remainingToasts] = pendingToasts;
                pendingToasts = remainingToasts;
                // Fork means we are creating a subprocess that will handle the display of a single toast
                yield fork(displayToast,firstToast);
                // Add little delay so that 2 concurrent toast requests aren't display at the same time
                yield call(delay,300);
            }
            else {
                yield call(delay,50);
            }
        }
    }

    // This toast saga is a composition of 2 smaller "sub-sagas" (we could also have used fork/spawn effects here, the difference is quite subtile: it depends if you want toastSaga to block)
    yield [
        call(toastRequestsWatcher),
        call(toastScheduler)
    ]
}

और रिड्यूसर:

const reducer = (state = [],event) => {
    switch (event.name) {
        case Names.TOAST_DISPLAYED:
            return [...state,event.data.toastData];
        case Names.TOAST_HIDDEN:
            return _.without(state,event.data.toastData);
        default:
            return state;
    }
};

प्रयोग

आप बस TOAST_DISPLAY_REQUESTEDघटनाओं को भेज सकते हैं। यदि आप 4 अनुरोधों को भेजते हैं, तो केवल 3 सूचनाएं प्रदर्शित की जाएंगी, और 1 अधिसूचना गायब होने के बाद 4 एक थोड़ा बाद में दिखाई देगा।

ध्यान दें कि मैं विशेष रूप TOAST_DISPLAY_REQUESTEDसे JSX से प्रेषण की अनुशंसा नहीं करता हूं । आप एक और गाथा जोड़ेंगे जो आपके पहले से मौजूद ऐप ईवेंट्स को सुनती है, और फिर उसे भेजती है TOAST_DISPLAY_REQUESTED: आपका घटक जो सूचना को ट्रिगर करता है, उसे अधिसूचना प्रणाली में कसकर युग्मित करने की आवश्यकता नहीं है।

निष्कर्ष

मेरा कोड सही नहीं है, लेकिन महीनों तक 0 बग के साथ उत्पादन में चलता है। Redux-saga और जनरेटर शुरू में थोड़े कठिन होते हैं लेकिन एक बार जब आप उन्हें समझ जाते हैं तो इस तरह का सिस्टम बनाना काफी आसान है।

अधिक जटिल नियमों को लागू करना और भी आसान है, जैसे:

  • जब बहुत सारी सूचनाएं "कतारबद्ध" होती हैं, तो प्रत्येक अधिसूचना के लिए कम प्रदर्शन-समय दें ताकि कतार का आकार तेजी से घट सके।
  • खिड़की के आकार में परिवर्तन का पता लगाएं, और तदनुसार प्रदर्शित सूचनाओं की अधिकतम संख्या को बदलें (उदाहरण के लिए, डेस्कटॉप = 3, फोन पोर्ट्रेट = 2, फोन लैंडस्केप = 1)

ईमानदारी से, गुड लक इस तरह के सामान को थ्रक्स के साथ ठीक से लागू करना है।

नोट आप बिल्कुल उसी तरह का काम कर सकते हैं जैसे कि redux-observable जो redux-saga के समान है। यह लगभग समान है और जनरेटर और RxJS के बीच स्वाद का मामला है।


18
काश, आपका उत्तर पहले आता था जब प्रश्न पूछा जाता था, क्योंकि मैं इस तरह के व्यावसायिक तर्क के लिए सागा साइड लाइब्रेरी का उपयोग करने से अधिक सहमत नहीं हो सकता। Reducers & Action Creators राज्य के बदलाव के लिए हैं। वर्कफ़्लोज़ राज्य संक्रमण कार्यों के समान नहीं हैं। वर्कफ़्लोज़ संक्रमण के माध्यम से कदम बढ़ाते हैं, लेकिन स्वयं संक्रमण नहीं होते हैं। Redux + React का अपने आप में अभाव है - यही कारण है कि Redux Saga इतना उपयोगी है।
एटिकस

4
धन्यवाद, मैं इन कारणों से redux-saga को लोकप्रिय बनाने के लिए अपना सर्वश्रेष्ठ करने की कोशिश करता हूं :) बहुत कम लोग सोचते हैं कि वर्तमान में redux-saga थ्रक्स के लिए सिर्फ एक प्रतिस्थापन है और यह न देखें कि redux-saga जटिल और विघटित वर्कफ़्लोज़ को कैसे सक्षम करता है
सेबस्टियन लॉबर ...

1
बिल्कुल सही। क्रियाएँ और Reducers सभी राज्य मशीन का हिस्सा हैं। कभी-कभी, जटिल वर्कफ़्लो के लिए, आपको राज्य मशीन को ऑर्केस्ट्रेट करने के लिए कुछ और चाहिए जो सीधे राज्य मशीन का हिस्सा नहीं है!
एटिक्स

2
क्रिया: पेलोड / घटनाओं को संक्रमण अवस्था में। Reducers: राज्य संक्रमण कार्य करता है। घटक: उपयोगकर्ता इंटरफेस राज्य को दर्शाते हैं। लेकिन एक प्रमुख टुकड़ा गायब है - आप कई बदलावों की प्रक्रिया का प्रबंधन कैसे करते हैं जो सभी के पास अपने तर्क हैं जो यह निर्धारित करते हैं कि आगे कौन सा संक्रमण करना है? Redux सागा!
एटिकस

2
@mrbrdo यदि आप मेरे उत्तर को ध्यान से पढ़ते हैं तो आप ध्यान देंगे कि अधिसूचना समयबाह्य के साथ वास्तव में नियंत्रित की जाती है yield call(delay,timeoutValue);: यह एक ही एपीआई नहीं है लेकिन इसका एक ही प्रभाव है
सेबस्टियन लॉबर

25

नमूना परियोजनाओं के साथ एक भंडार

वर्तमान में चार नमूना परियोजनाएं हैं:

  1. Async कोड इनलाइन लिखना
  2. Async एक्शन क्रिएटर को निकालना
  3. Redux Thunk का उपयोग करें
  4. Redux सागा का उपयोग करें

स्वीकृत जवाब कमाल का है।

लेकिन कुछ गायब है:

  1. कोई रन करने योग्य नमूना प्रोजेक्ट नहीं, बस कुछ कोड स्निपेट।
  2. अन्य विकल्पों के लिए कोई नमूना कोड नहीं, जैसे:
    1. Redux सागा

इसलिए मैंने लापता चीजों को जोड़ने के लिए हैलो Async रिपॉजिटरी बनाई :

  1. चल परियोजनाएं। आप उन्हें संशोधन के बिना डाउनलोड और चला सकते हैं।
  2. अधिक विकल्पों के लिए नमूना कोड प्रदान करें:

Redux सागा

स्वीकृत उत्तर पहले से ही Async कोड इनलाइन, Async एक्शन जेनरेटर और Redux Thunk के लिए नमूना कोड स्निपेट प्रदान करता है। पूर्णता के लिए, मैं Redux सागा के लिए कोड स्निपेट प्रदान करता हूं:

// actions.js

export const showNotification = (id, text) => {
  return { type: 'SHOW_NOTIFICATION', id, text }
}

export const hideNotification = (id) => {
  return { type: 'HIDE_NOTIFICATION', id }
}

export const showNotificationWithTimeout = (text) => {
  return { type: 'SHOW_NOTIFICATION_WITH_TIMEOUT', text }
}

क्रियाएं सरल और शुद्ध हैं।

// component.js

import { connect } from 'react-redux'

// ...

this.props.showNotificationWithTimeout('You just logged in.')

// ...

export default connect(
  mapStateToProps,
  { showNotificationWithTimeout }
)(MyComponent)

घटक के साथ कुछ भी विशेष नहीं है।

// sagas.js

import { takeEvery, delay } from 'redux-saga'
import { put } from 'redux-saga/effects'
import { showNotification, hideNotification } from './actions'

// Worker saga
let nextNotificationId = 0
function* showNotificationWithTimeout (action) {
  const id = nextNotificationId++
  yield put(showNotification(id, action.text))
  yield delay(5000)
  yield put(hideNotification(id))
}

// Watcher saga, will invoke worker saga above upon action 'SHOW_NOTIFICATION_WITH_TIMEOUT'
function* notificationSaga () {
  yield takeEvery('SHOW_NOTIFICATION_WITH_TIMEOUT', showNotificationWithTimeout)
}

export default notificationSaga

सगा ईएस 6 जेनरेटर पर आधारित हैं

// index.js

import createSagaMiddleware from 'redux-saga'
import saga from './sagas'

const sagaMiddleware = createSagaMiddleware()

const store = createStore(
  reducer,
  applyMiddleware(sagaMiddleware)
)

sagaMiddleware.run(saga)

Redux Thunk की तुलना में

पेशेवरों

  • आप कॉलबैक नरक में समाप्त नहीं होते हैं।
  • आप अपने एसिंक्रोनस प्रवाह का आसानी से परीक्षण कर सकते हैं।
  • आपके कर्म शुद्ध रहते हैं।

विपक्ष

  • यह ES6 जेनरेटर पर निर्भर करता है जो अपेक्षाकृत नया है।

यदि कोड स्निपेट ऊपर दिए गए हैं तो कृपया रन करने योग्य परियोजना को देखें ।


23

आप इसे redux-thunk के साथ कर सकते हैं । सेटटाउट जैसी एसिंक्स क्रियाओं के लिए Redux दस्तावेज़ में एक मार्गदर्शिका है


बस एक त्वरित अनुवर्ती प्रश्न, जब मिडलवेयर applyMiddleware(ReduxPromise, thunk)(createStore)का उपयोग किया जाता है तो यह है कि आप कई मिडलवेयर (कोमा अलग हो गए हैं?) को जोड़ सकते हैं क्योंकि मुझे लगता है कि थंक काम नहीं कर सकता।
इलैजा

1
@ इल्जा यह काम करना चाहिए:const store = createStore(reducer, applyMiddleware([ReduxPromise, thunk]));
जीनियसरियर

22

मैं एसएएम पैटर्न पर भी एक नज़र डालने की सलाह दूंगा

एसएएम पैटर्न एक "अगली-कार्रवाई-विधेय" को शामिल करने की वकालत करता है जहां (5 सेकंड के बाद स्वचालित रूप से सूचनाएँ "गायब हो जाती हैं" जैसे मॉडल अपडेट होने के बाद चालू हो जाते हैं (एसएएम मॉडल ~ रिड्यूसर स्टेट + स्टोर)।

पैटर्न एक समय में क्रियाओं और मॉडल म्यूटेशन की वकालत करता है, क्योंकि मॉडल "नियंत्रण" की "स्थिति" जो क्रियाओं को सक्षम और / या स्वचालित रूप से अगली कार्रवाई के द्वारा निष्पादित की जाती है। आप बस यह अनुमान नहीं लगा सकते हैं कि (सामान्य रूप से) राज्य किस स्थिति में कार्रवाई करने से पहले होगा और इसलिए आपकी अगली अपेक्षित कार्रवाई की अनुमति दी जाएगी या नहीं।

उदाहरण के लिए कोड,

export function showNotificationWithTimeout(dispatch, text) {
  const id = nextNotificationId++
  dispatch(showNotification(id, text))

  setTimeout(() => {
    dispatch(hideNotification(id))
  }, 5000)
}

एसएएम के साथ अनुमति नहीं दी जाएगी, क्योंकि यह तथ्य कि एक छिपाने की क्रिया को भेजा जा सकता है, "शो नॉटिकेशन: ट्रू" मान को सफलतापूर्वक स्वीकार करने वाले मॉडल पर निर्भर है। मॉडल के अन्य भाग हो सकते हैं जो इसे स्वीकार करने से रोकता है और इसलिए, छिपाने की क्रिया को ट्रिगर करने का कोई कारण नहीं होगा।

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

यदि आप चाहें तो आप हमें गटर में शामिल कर सकते हैं। यहाँ एक एसएएम प्रारंभ गाइड भी उपलब्ध है


मैंने अभी तक केवल सतह को खरोंच किया है, लेकिन पहले से ही एसएएम पैटर्न से रोमांचित हूं। V = S( vm( M.present( A(data) ) ), nap(M))बस सुंदर है। अपने विचारों और अनुभव को साझा करने के लिए धन्यवाद। मैं गहरी खुदाई करूँगा।

@ftor, धन्यवाद! जब मैंने इसे पहली बार लिखा, तो मुझे भी ऐसा ही एहसास हुआ। मैंने लगभग एक साल के लिए उत्पादन में एसएएम का उपयोग किया है, और मैं उस समय के बारे में नहीं सोच सकता हूं जहां मुझे लगा कि मुझे सैम को लागू करने के लिए एक पुस्तकालय की आवश्यकता है (यहां तक ​​कि vdom, हालांकि मैं देख सकता हूं कि इसका उपयोग कब किया जा सकता है)। कोड की सिर्फ एक पंक्ति, बस! एसएएम isomorphic कोड का उत्पादन करता है, कोई अस्पष्टता नहीं है जहां async कॉल से कैसे निपटना है ... मैं उस समय के बारे में नहीं सोच सकता जहां मैं हालांकि, मैं क्या कर रहा हूं?
मेटाप्रोग्रामर

एसएएम एक सच्चा सॉफ्टवेयर इंजीनियरिंग पैटर्न है (बस इसके साथ एक एलेक्सा एसडीके का उत्पादन किया गया है)। यह टीएलए + पर आधारित है और प्रत्येक डेवलपर के लिए उस अविश्वसनीय कार्य की शक्ति लाने का प्रयास करता है। एसएएम तीन अनुमानों को सही करता है जो (बहुत अधिक) हर कोई दशकों से उपयोग कर रहा है: - क्रियाएं अनुप्रयोग स्थिति में फेरबदल कर सकती हैं - असाइनमेंट म्यूटेशन के बराबर हैं - कोई सटीक परिभाषा नहीं है कि प्रोग्रामिंग कदम क्या है (जैसे = a = b * ca कदम , 1 / रीड बी, सी 2 / कम्प्यूट बी * सी, 3 / परिणाम तीन अलग-अलग चरणों के साथ असाइन करें;
मेटाप्रोग्रामर

20

विभिन्न लोकप्रिय दृष्टिकोणों (एक्शन क्रिएटर्स, थ्रक्स, साग, एपिक्स, इफेक्ट्स, कस्टम मिडलवेयर) की कोशिश करने के बाद, मुझे अभी भी लगा कि शायद इसमें सुधार की गुंजाइश थी, इसलिए मैंने इस ब्लॉग लेख में अपनी यात्रा का दस्तावेजीकरण किया, जिसमें मैंने अपना व्यावसायिक तर्क कहाँ रखा है? एक प्रतिक्रिया / Redux आवेदन?

यहां की चर्चाओं की तरह, मैंने विभिन्न दृष्टिकोणों के विपरीत और तुलना करने की कोशिश की। आखिरकार इसने मुझे एक नया पुस्तकालय रिड्यूस-लॉजिक शुरू करने के लिए प्रेरित किया, जो महाकाव्यों, सागाओं, कस्टम मिडलवेयर से प्रेरणा लेता है।

यह आपको async IO करने के लिए एक तरीका प्रदान करने, सत्यापित करने, अधिकृत करने, साथ ही साथ अवरोधन करने की अनुमति देता है।

कुछ सामान्य कार्यक्षमता को केवल डिबगिंग, थ्रॉटलिंग, कैंसलेशन की तरह घोषित किया जा सकता है, और केवल नवीनतम अनुरोध (टेकलैस्ट) से प्रतिक्रिया का उपयोग कर सकते हैं। Redux- लॉजिक आपके कोड को आपके लिए यह कार्यक्षमता प्रदान करता है।

हालांकि, आपको अपने मुख्य व्यवसाय तर्क को लागू करने के लिए स्वतंत्र करता है जो आपको पसंद है। जब तक आप नहीं चाहते हैं, आपको वेधशाला या जनरेटर का उपयोग करने की आवश्यकता नहीं है। कार्यों और कॉलबैक, वादों, async फ़ंक्शन (async / प्रतीक्षा), आदि का उपयोग करें

सरल 5s अधिसूचना करने के लिए कोड कुछ इस तरह होगा:

const notificationHide = createLogic({
  // the action type that will trigger this logic
  type: 'NOTIFICATION_DISPLAY',
  
  // your business logic can be applied in several
  // execution hooks: validate, transform, process
  // We are defining our code in the process hook below
  // so it runs after the action hit reducers, hide 5s later
  process({ getState, action }, dispatch) {
    setTimeout(() => {
      dispatch({ type: 'NOTIFICATION_CLEAR' });
    }, 5000);
  }
});
    

मेरे रेपो में मेरे पास एक अधिक उन्नत अधिसूचना उदाहरण है जो कि सेबस्टियन लॉबर द्वारा वर्णित के समान काम करता है जहां आप प्रदर्शन को एन आइटम तक सीमित कर सकते हैं और किसी भी कतार के माध्यम से घुमा सकते हैं। Redux- तर्क अधिसूचना उदाहरण

मेरे पास विभिन्न प्रकार के Redux- लॉजिक jsfiddle लाइव उदाहरण हैं और साथ ही पूर्ण उदाहरण हैं । मैं डॉक्स और उदाहरणों पर काम करना जारी रख रहा हूं।

मुझे आपकी प्रतिक्रिया सुनना अच्छा लगेगा।


मुझे यकीन नहीं है कि मुझे आपकी लाइब्रेरी पसंद है लेकिन मैं आपके लेख को पसंद करता हूँ! अच्छा हुआ, यार! आपने दूसरों के समय को बचाने के लिए पर्याप्त काम किया है।
टायलर लॉन्ग

2
मैंने यहाँ redux- लॉजिक के लिए एक नमूना प्रोजेक्ट बनाया: github.com/tylerlong/hello-async/tree/master/redux-logic मुझे लगता है कि यह एक अच्छी तरह से डिज़ाइन किया गया सॉफ़्टवेयर है और मुझे अन्य की तुलना में कोई बड़ा नुकसान नहीं दिखता है विकल्प।
टायलर लॉन्ग

9

मैं समझता हूं कि यह प्रश्न थोड़ा पुराना है, लेकिन मैं redux-observable aka का उपयोग कर एक और समाधान पेश करने जा रहा हूं । महाकाव्य।

आधिकारिक दस्तावेज का हवाला देते हुए:

Redux-Observable क्या है?

रेडक्स के लिए RxJS 5-आधारित मिडलवेयर। साइड इफेक्ट और अधिक बनाने के लिए async क्रियाओं को लिखें और रद्द करें।

एक महाकाव्य Redux-Observable का मुख्य आदिम है।

यह एक फ़ंक्शन है जो क्रियाओं की एक धारा लेता है और क्रियाओं की एक धारा लौटाता है। में क्रियाएँ, क्रिया।

अधिक या कम शब्दों में, आप एक फ़ंक्शन बना सकते हैं जो स्ट्रीम के माध्यम से क्रियाओं को प्राप्त करता है और फिर क्रियाओं की एक नई धारा लौटाता है (सामान्य दुष्प्रभावों जैसे टाइमआउट, देरी, अंतराल और अनुरोधों का उपयोग करके)।

मुझे कोड पोस्ट करने दें और फिर इसके बारे में थोड़ा और समझाएं

store.js

import {createStore, applyMiddleware} from 'redux'
import {createEpicMiddleware} from 'redux-observable'
import {Observable} from 'rxjs'
const NEW_NOTIFICATION = 'NEW_NOTIFICATION'
const QUIT_NOTIFICATION = 'QUIT_NOTIFICATION'
const NOTIFICATION_TIMEOUT = 2000

const initialState = ''
const rootReducer = (state = initialState, action) => {
  const {type, message} = action
  console.log(type)
  switch(type) {
    case NEW_NOTIFICATION:
      return message
    break
    case QUIT_NOTIFICATION:
      return initialState
    break
  }

  return state
}

const rootEpic = (action$) => {
  const incoming = action$.ofType(NEW_NOTIFICATION)
  const outgoing = incoming.switchMap((action) => {
    return Observable.of(quitNotification())
      .delay(NOTIFICATION_TIMEOUT)
      //.takeUntil(action$.ofType(NEW_NOTIFICATION))
  });

  return outgoing;
}

export function newNotification(message) {
  return ({type: NEW_NOTIFICATION, message})
}
export function quitNotification(message) {
  return ({type: QUIT_NOTIFICATION, message});
}

export const configureStore = () => createStore(
  rootReducer,
  applyMiddleware(createEpicMiddleware(rootEpic))
)

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {configureStore} from './store.js'
import {Provider} from 'react-redux'

const store = configureStore()

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

App.js

import React, { Component } from 'react';
import {connect} from 'react-redux'
import {newNotification} from './store.js'

class App extends Component {

  render() {
    return (
      <div className="App">
        {this.props.notificationExistance ? (<p>{this.props.notificationMessage}</p>) : ''}
        <button onClick={this.props.onNotificationRequest}>Click!</button>
      </div>
    );
  }
}

const mapStateToProps = (state) => {
  return {
    notificationExistance : state.length > 0,
    notificationMessage : state
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onNotificationRequest: () => dispatch(newNotification(new Date().toDateString()))
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(App)

इस समस्या को हल करने के लिए कुंजी कोड पाई जितना आसान है जितना कि आप देख सकते हैं, केवल एक चीज जो अन्य उत्तरों से अलग दिखाई देती है वह है फंक्शन rootEpic।

बिंदु 1. सागाओं के साथ, आपको शीर्ष स्तर के फ़ंक्शन को प्राप्त करने के लिए महाकाव्यों को संयोजित करना होगा जो क्रियाओं की एक धारा प्राप्त करता है और क्रियाओं की एक धारा लौटाता है, इसलिए आप इसे मिडलवेयर फ़ैक्टरी createEpicMiddleware के साथ उपयोग कर सकते हैं । हमारे मामले में हमें केवल एक की आवश्यकता होती है, इसलिए हमारे पास केवल हमारी जड़ है इसलिए हमें कुछ भी संयोजित नहीं करना है, लेकिन यह तथ्य जानना अच्छा है।

बिंदु 2. हमारी जड़ जो साइड इफेक्ट्स लॉजिक के बारे में ध्यान रखता है, केवल कोड की लगभग 5 पंक्तियाँ लेता है जो कि कमाल है! तथ्य यह है कि बहुत ज्यादा घोषणात्मक भी शामिल है!

बिंदु 3. लाइन द्वारा रूट रूट व्याख्या (टिप्पणियों में)

const rootEpic = (action$) => {
  // sets the incoming constant as a stream 
  // of actions with  type NEW_NOTIFICATION
  const incoming = action$.ofType(NEW_NOTIFICATION)
  // Merges the "incoming" stream with the stream resulting for each call
  // This functionality is similar to flatMap (or Promise.all in some way)
  // It creates a new stream with the values of incoming and 
  // the resulting values of the stream generated by the function passed
  // but it stops the merge when incoming gets a new value SO!,
  // in result: no quitNotification action is set in the resulting stream
  // in case there is a new alert
  const outgoing = incoming.switchMap((action) => {
    // creates of observable with the value passed 
    // (a stream with only one node)
    return Observable.of(quitNotification())
      // it waits before sending the nodes 
      // from the Observable.of(...) statement
      .delay(NOTIFICATION_TIMEOUT)
  });
  // we return the resulting stream
  return outgoing;
}

मुझे उम्मीद है यह मदद करेगा!


क्या आप बता सकते हैं कि विशिष्ट एपीई विधियां यहां क्या कर रही हैं, जैसे कि switchMap?
दिमित्री ज़ैतसेव

1
हम विंडोज पर अपने रिएक्ट नेटिव ऐप में redux-observable का उपयोग कर रहे हैं। यह एक जटिल, अत्यधिक अतुल्यकालिक समस्या का एक सुरुचिपूर्ण कार्यान्वयन समाधान है और उनके Gitter चैनल और GitHub मुद्दों के माध्यम से शानदार समर्थन है। जटिलता की अतिरिक्त परत केवल इसके लायक है यदि आप सटीक समस्या पर पहुंचते हैं, तो इसका हल निश्चित रूप से है।
मैट हरगेट

8

यह इतना कठिन क्यों होना चाहिए? यह सिर्फ यूआई तर्क है। अधिसूचना डेटा सेट करने के लिए एक समर्पित कार्रवाई का उपयोग करें:

dispatch({ notificationData: { message: 'message', expire: +new Date() + 5*1000 } })

और इसे प्रदर्शित करने के लिए एक समर्पित घटक:

const Notifications = ({ notificationData }) => {
    if(notificationData.expire > this.state.currentTime) {
      return <div>{notificationData.message}</div>
    } else return null;
}

इस मामले में प्रश्न "आप पुरानी स्थिति को कैसे साफ करते हैं?", "समय बदलने पर एक घटक को कैसे सूचित करें" होना चाहिए

आप कुछ समय कार्रवाई को लागू कर सकते हैं जो किसी घटक से सेटटाइमआउट पर प्रेषित की जाती है।

हो सकता है कि जब भी कोई नया नोटिफिकेशन दिखाया जाता है, तो उसे साफ करना ठीक है।

वैसे भी, setTimeoutकहीं तो होना चाहिए ना? एक घटक में क्यों नहीं करना है

setTimeout(() => this.setState({ currentTime: +new Date()}), 
           this.props.notificationData.expire-(+new Date()) )

प्रेरणा यह है कि "अधिसूचना फीका बाहर" कार्यक्षमता वास्तव में एक यूआई चिंता है। तो यह आपके व्यावसायिक तर्क के लिए परीक्षण को सरल करता है।

यह परीक्षण करने के लिए समझ में नहीं आता है कि यह कैसे लागू किया जाता है। यह केवल तभी सत्यापित करता है कि अधिसूचना कब समाप्त होनी चाहिए। इस प्रकार स्टब के लिए कम कोड, तेज परीक्षण, क्लीनर कोड।


1
यह शीर्ष उत्तर होना चाहिए।
mmla

6

यदि आप चयनात्मक क्रियाओं पर टाइमआउट हैंडलिंग चाहते हैं, तो आप मिडलवेयर की कोशिश कर सकते हैं दृष्टिकोण की । मुझे वादा आधारित कार्यों को चुनने के लिए इसी तरह की समस्या का सामना करना पड़ा और यह समाधान अधिक लचीला था।

आप कहते हैं कि आपका एक्शन निर्माता इस तरह दिखता है:

//action creator
buildAction = (actionData) => ({
    ...actionData,
    timeout: 500
})

टाइमआउट उपरोक्त कार्रवाई में कई मान रख सकता है

  • एमएस में संख्या - एक विशिष्ट टाइमआउट अवधि के लिए
  • सच - एक निरंतर टाइमआउट अवधि के लिए। (मिडलवेयर में संभाला)
  • अपरिभाषित - तत्काल प्रेषण के लिए

आपका मिडलवेयर कार्यान्वयन इस तरह दिखाई देगा:

//timeoutMiddleware.js
const timeoutMiddleware = store => next => action => {

  //If your action doesn't have any timeout attribute, fallback to the default handler
  if(!action.timeout) {
    return next (action)
  }

  const defaultTimeoutDuration = 1000;
  const timeoutDuration = Number.isInteger(action.timeout) ? action.timeout || defaultTimeoutDuration;

//timeout here is called based on the duration defined in the action.
  setTimeout(() => {
    next (action)
  }, timeoutDuration)
}

अब आप redux का उपयोग करके इस मिडलवेयर लेयर के माध्यम से अपने सभी कार्यों को रूट कर सकते हैं।

createStore(reducer, applyMiddleware(timeoutMiddleware))

आप यहां कुछ ऐसे ही उदाहरण पा सकते हैं


5

ऐसा करने का उपयुक्त तरीका Redux Thunk का उपयोग करना है जो Redux Thunk प्रलेखन के अनुसार Redux के लिए एक लोकप्रिय मिडलवेयर है:

"Redux Thunk मिडलवेयर आपको एक्शन क्रिएटर्स लिखने की अनुमति देता है जो एक एक्शन के बजाय एक फंक्शन लौटाते हैं। थंक का उपयोग किसी एक्शन के प्रेषण में देरी करने के लिए किया जा सकता है, या केवल एक निश्चित स्थिति के पूरा होने पर प्रेषण के लिए किया जाता है। इनर फंक्शन स्टोर के तरीकों को प्राप्त करता है। प्रेषण और पैरामीटर के रूप में गेटस्ट करें "।

इसलिए मूल रूप से यह एक फ़ंक्शन देता है, और आप अपने प्रेषण में देरी कर सकते हैं या इसे एक स्थिति में रख सकते हैं।

तो कुछ इस तरह से आप के लिए काम करने जा रहा है:

import ReduxThunk from 'redux-thunk';

const INCREMENT_COUNTER = 'INCREMENT_COUNTER';

function increment() {
  return {
    type: INCREMENT_COUNTER
  };
}

function incrementAsync() {
  return dispatch => {
    setTimeout(() => {
      // Yay! Can invoke sync or async actions with `dispatch`
      dispatch(increment());
    }, 5000);
  };
}


3

Redux अपने आप में एक सुंदर वर्बोज़ लाइब्रेरी है, और इस तरह के सामान के लिए आपको Redux-thunk जैसी किसी चीज़ का उपयोग करना होगा, जो एक dispatchफंक्शन देगा, जिससे आप कई सेकंड के बाद नोटिफिकेशन को बंद कर पाएंगे।

मैंने वर्बोसिटी और कंपोज़ीबिलिटी जैसे मुद्दों को दूर करने के लिए एक लाइब्रेरी बनाई है , और आपका उदाहरण निम्नलिखित की तरह दिखेगा:

import { createTile, createSyncTile } from 'redux-tiles';
import { sleep } from 'delounce';

const notifications = createSyncTile({
  type: ['ui', 'notifications'],
  fn: ({ params }) => params.data,
  // to have only one tile for all notifications
  nesting: ({ type }) => [type],
});

const notificationsManager = createTile({
  type: ['ui', 'notificationManager'],
  fn: ({ params, dispatch, actions }) => {
    dispatch(actions.ui.notifications({ type: params.type, data: params.data }));
    await sleep(params.timeout || 5000);
    dispatch(actions.ui.notifications({ type: params.type, data: null }));
    return { closed: true };
  },
  nesting: ({ type }) => [type],
});

इसलिए हम async कार्रवाई के अंदर सूचनाएं दिखाने के लिए सिंक क्रियाओं की रचना करते हैं, जो पृष्ठभूमि के बारे में कुछ जानकारी का अनुरोध कर सकते हैं, या बाद में जांच सकते हैं कि अधिसूचना मैन्युअल रूप से बंद की गई थी या नहीं।

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