हमें Redux में async फ्लो के लिए मिडलवेयर की आवश्यकता क्यों है?


686

डॉक्स के अनुसार, "मिडिलवेयर के बिना, रेडक्स स्टोर केवल तुल्यकालिक डेटा प्रवाह का समर्थन करता है" । मुझे समझ नहीं आया कि ऐसा क्यों है। कंटेनर घटक async एपीआई, और फिर dispatchक्रियाओं को क्यों नहीं कर सकता है ?

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

एक फ़ील्ड और एक बटन

import * as React from 'react';
import * as Redux from 'redux';
import { Provider, connect } from 'react-redux';

const ActionTypes = {
    STARTED_UPDATING: 'STARTED_UPDATING',
    UPDATED: 'UPDATED'
};

class AsyncApi {
    static getFieldValue() {
        const promise = new Promise((resolve) => {
            setTimeout(() => {
                resolve(Math.floor(Math.random() * 100));
            }, 1000);
        });
        return promise;
    }
}

class App extends React.Component {
    render() {
        return (
            <div>
                <input value={this.props.field}/>
                <button disabled={this.props.isWaiting} onClick={this.props.update}>Fetch</button>
                {this.props.isWaiting && <div>Waiting...</div>}
            </div>
        );
    }
}
App.propTypes = {
    dispatch: React.PropTypes.func,
    field: React.PropTypes.any,
    isWaiting: React.PropTypes.bool
};

const reducer = (state = { field: 'No data', isWaiting: false }, action) => {
    switch (action.type) {
        case ActionTypes.STARTED_UPDATING:
            return { ...state, isWaiting: true };
        case ActionTypes.UPDATED:
            return { ...state, isWaiting: false, field: action.payload };
        default:
            return state;
    }
};
const store = Redux.createStore(reducer);
const ConnectedApp = connect(
    (state) => {
        return { ...state };
    },
    (dispatch) => {
        return {
            update: () => {
                dispatch({
                    type: ActionTypes.STARTED_UPDATING
                });
                AsyncApi.getFieldValue()
                    .then(result => dispatch({
                        type: ActionTypes.UPDATED,
                        payload: result
                    }));
            }
        };
    })(App);
export default class extends React.Component {
    render() {
        return <Provider store={store}><ConnectedApp/></Provider>;
    }
}

जब निर्यात किया गया घटक प्रदान किया जाता है, तो मैं बटन पर क्लिक कर सकता हूं और इनपुट सही ढंग से अपडेट किया जाता है।

कॉल updateमें फ़ंक्शन को नोट connectकरें। यह एक एक्शन भेजता है जो ऐप को बताता है कि वह अपडेट हो रहा है, और फिर एक एसिंक्स कॉल करता है। कॉल समाप्त होने के बाद, प्रदान किया गया मान दूसरी कार्रवाई के पेलोड के रूप में भेजा जाता है।

इस दृष्टिकोण के साथ क्या गलत है? जैसा कि प्रलेखन से पता चलता है, मैं Redux Thunk या Redux Promise का उपयोग क्यों करना चाहूंगा?

संपादित करें: मैंने सुराग के लिए Redux रेपो की खोज की, और पाया कि एक्शन क्रिएटर्स को अतीत में शुद्ध कार्य करने की आवश्यकता थी। उदाहरण के लिए, यहाँ एक उपयोगकर्ता async डेटा प्रवाह के लिए एक बेहतर स्पष्टीकरण प्रदान करने की कोशिश कर रहा है:

एक्शन क्रिएटर अपने आप में अभी भी एक शुद्ध कार्य है, लेकिन यह जिस थंक फंक्शन की वापसी करता है, उसे करने की आवश्यकता नहीं होती है, और यह हमारे समान कॉल को कर सकता है

एक्शन क्रिएटर्स को अब शुद्ध होने की आवश्यकता नहीं है। इसलिए, थंक / वादा मिडलवेयर निश्चित रूप से अतीत में आवश्यक था, लेकिन ऐसा लगता है कि अब ऐसा नहीं है?


53
एक्शन क्रिएटर्स को कभी भी शुद्ध कार्य करने की आवश्यकता नहीं थी। यह डॉक्स में एक गलती थी, एक निर्णय नहीं जो बदल गया।
दान अब्रामोव

1
परीक्षण के लिए @DanAbramov हालांकि यह एक अच्छा अभ्यास हो सकता है। Redux-saga इसे अनुमति देता है: stackoverflow.com/a/34623840/82609
सेबस्टियन लोरबर

जवाबों:


701

इस दृष्टिकोण के साथ क्या गलत है? जैसा कि प्रलेखन से पता चलता है, मैं Redux Thunk या Redux Promise का उपयोग क्यों करना चाहूंगा?

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

अधिक विस्तृत वॉकथ्रू के लिए आप "कैसे एक टाइमआउट के साथ एक Redux कार्रवाई को भेजने के लिए" मेरा जवाब पढ़ सकते हैं ।

Redux Thunk या Redux Promise जैसे मिडलवेयर आपको थ्रेड्स या वादों को भेजने के लिए सिर्फ "सिंटेक्स शुगर" देते हैं, लेकिन आपको इसका उपयोग करने की आवश्यकता नहीं है

इसलिए, किसी भी मिडलवेयर के बिना, आपका एक्शन क्रिएटर दिख सकता है

// action creator
function loadData(dispatch, userId) { // needs to dispatch, so it is first argument
  return fetch(`http://data.com/${userId}`)
    .then(res => res.json())
    .then(
      data => dispatch({ type: 'LOAD_DATA_SUCCESS', data }),
      err => dispatch({ type: 'LOAD_DATA_FAILURE', err })
    );
}

// component
componentWillMount() {
  loadData(this.props.dispatch, this.props.userId); // don't forget to pass dispatch
}

लेकिन थंक मिडलवेयर के साथ आप इसे इस तरह लिख सकते हैं:

// action creator
function loadData(userId) {
  return dispatch => fetch(`http://data.com/${userId}`) // Redux Thunk handles these
    .then(res => res.json())
    .then(
      data => dispatch({ type: 'LOAD_DATA_SUCCESS', data }),
      err => dispatch({ type: 'LOAD_DATA_FAILURE', err })
    );
}

// component
componentWillMount() {
  this.props.dispatch(loadData(this.props.userId)); // dispatch like you usually do
}

इसलिए कोई बहुत बड़ा अंतर नहीं है। एक चीज जो मुझे बाद के दृष्टिकोण के बारे में पसंद है, वह यह है कि घटक को इस बात की परवाह नहीं है कि एक्शन रचनाकार असंस है। यह बस dispatchसामान्य रूप से कॉल करता है , यह mapDispatchToPropsऐसे एक्शन क्रिएटर को शॉर्ट सिंटैक्स के साथ बाँधने के लिए भी उपयोग कर सकता है , आदि घटकों को यह नहीं पता है कि एक्शन क्रिएटर को कैसे लागू किया जाता है, और आप अलग-अलग एस्कॉन्क्स एप्रोच (Redux Thunk, Redux Promise, Redux Raga) के बीच स्विच कर सकते हैं ) घटकों को बदलने के बिना। दूसरी ओर, पूर्व, स्पष्ट दृष्टिकोण के साथ, आपके घटकों को वास्तव में पता है कि एक विशिष्ट कॉल एसिंक्रोनस है, और इसकी आवश्यकता हैdispatch कुछ सम्मेलन (उदाहरण के लिए, एक सिंक पैरामीटर के रूप में) द्वारा पारित करने की है।

यह भी सोचें कि यह कोड कैसे बदलेगा। मान लें कि हम एक दूसरे डेटा लोड करने का कार्य करना चाहते हैं, और उन्हें एक एक्शन क्रिएटर में संयोजित करना चाहते हैं।

पहले दृष्टिकोण के साथ हमें यह सोचने की ज़रूरत है कि हम किस प्रकार के एक्शन निर्माता को बुला रहे हैं:

// action creators
function loadSomeData(dispatch, userId) {
  return fetch(`http://data.com/${userId}`)
    .then(res => res.json())
    .then(
      data => dispatch({ type: 'LOAD_SOME_DATA_SUCCESS', data }),
      err => dispatch({ type: 'LOAD_SOME_DATA_FAILURE', err })
    );
}
function loadOtherData(dispatch, userId) {
  return fetch(`http://data.com/${userId}`)
    .then(res => res.json())
    .then(
      data => dispatch({ type: 'LOAD_OTHER_DATA_SUCCESS', data }),
      err => dispatch({ type: 'LOAD_OTHER_DATA_FAILURE', err })
    );
}
function loadAllData(dispatch, userId) {
  return Promise.all(
    loadSomeData(dispatch, userId), // pass dispatch first: it's async
    loadOtherData(dispatch, userId) // pass dispatch first: it's async
  );
}


// component
componentWillMount() {
  loadAllData(this.props.dispatch, this.props.userId); // pass dispatch first
}

Redux Thunk एक्शन क्रिएटरों के साथ dispatchअन्य एक्शन क्रिएटर्स का परिणाम हो सकता है और यह भी नहीं सोचना चाहिए कि क्या वे सिंक्रोनस या एसिंक्रोनस हैं:

// action creators
function loadSomeData(userId) {
  return dispatch => fetch(`http://data.com/${userId}`)
    .then(res => res.json())
    .then(
      data => dispatch({ type: 'LOAD_SOME_DATA_SUCCESS', data }),
      err => dispatch({ type: 'LOAD_SOME_DATA_FAILURE', err })
    );
}
function loadOtherData(userId) {
  return dispatch => fetch(`http://data.com/${userId}`)
    .then(res => res.json())
    .then(
      data => dispatch({ type: 'LOAD_OTHER_DATA_SUCCESS', data }),
      err => dispatch({ type: 'LOAD_OTHER_DATA_FAILURE', err })
    );
}
function loadAllData(userId) {
  return dispatch => Promise.all(
    dispatch(loadSomeData(userId)), // just dispatch normally!
    dispatch(loadOtherData(userId)) // just dispatch normally!
  );
}


// component
componentWillMount() {
  this.props.dispatch(loadAllData(this.props.userId)); // just dispatch normally!
}

इस दृष्टिकोण के साथ, यदि आप बाद में अपने एक्शन क्रिएटर्स को वर्तमान Redux स्थिति में देखना चाहते हैं, तो आप getStateकॉलिंग कोड को संशोधित किए बिना दूसरे तर्क का उपयोग कर सकते हैं :

function loadSomeData(userId) {
  // Thanks to Redux Thunk I can use getState() here without changing callers
  return (dispatch, getState) => {
    if (getState().data[userId].isLoaded) {
      return Promise.resolve();
    }

    fetch(`http://data.com/${userId}`)
      .then(res => res.json())
      .then(
        data => dispatch({ type: 'LOAD_SOME_DATA_SUCCESS', data }),
        err => dispatch({ type: 'LOAD_SOME_DATA_FAILURE', err })
      );
  }
}

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

// I can change it to be a regular action creator without touching callers
function loadSomeData(userId) {
  return {
    type: 'LOAD_SOME_DATA_SUCCESS',
    data: localStorage.getItem('my-data')
  }
}

इसलिए Redux Thunk या Redux Promise जैसे मिडलवेयर का उपयोग करने का लाभ यह है कि घटकों को इस बात की जानकारी नहीं होती है कि एक्शन क्रिएटर्स को कैसे लागू किया जाता है, और क्या वे Redux स्टेट की परवाह करते हैं, चाहे वे सिंक्रोनस या एसिंक्रोनस हों, और चाहे वे एक्शन क्रिएटर कहें या नहीं। । नकारात्मक पक्ष थोड़ा अप्रत्यक्ष है, लेकिन हम मानते हैं कि यह वास्तविक अनुप्रयोगों में इसके लायक है।

अंत में, Redux Thunk और मित्र Redux ऐप्स में अतुल्यकालिक अनुरोधों के लिए केवल एक संभावित दृष्टिकोण है। एक और दिलचस्प दृष्टिकोण Redux Saga है जो आपको लंबे समय से चल रहे डेमॉन ("सागास") को परिभाषित करने देता है जो क्रियाओं को आते हैं और आउटपुट करने से पहले अनुरोधों को बदलते या निष्पादित करते हैं। यह एक्शन क्रिएटर्स से तर्क को सागा में स्थानांतरित करता है। आप इसे देखना चाहते हैं, और बाद में आपको सबसे ज्यादा सूट करना है।

मैंने सुराग के लिए Redux रेपो की खोज की, और पाया कि एक्शन क्रिएटर्स को अतीत में शुद्ध कार्य करने की आवश्यकता थी।

यह गलत है। डॉक्स ने यह कहा, लेकिन डॉक्स गलत थे।
एक्शन क्रिएटर्स को कभी भी शुद्ध कार्य करने की आवश्यकता नहीं थी।
हमने यह दर्शाने के लिए डॉक्स तय किए।


57
हो सकता है कि डैन के विचार कहने का छोटा तरीका है: मिडिलवेयर केंद्रीयकृत दृष्टिकोण है, इस तरह से आप अपने घटकों को सरल और सामान्यीकृत रख सकते हैं और एक स्थान पर डेटा प्रवाह को नियंत्रित कर सकते हैं। यदि आप बड़े ऐप को बनाए रखते हैं तो आपको इसका आनंद मिलेगा =)
सेर्गेई लैपिन

3
@asdfasdfads मैं नहीं देखता कि यह काम क्यों नहीं करेगा। यह ठीक उसी तरह काम करेगा; कार्रवाई के alertबाद dispatch()आईएनजी डाल दिया ।
दान अब्रामोव

9
आपके पहले कोड उदाहरण में दंड रेखा loadData(this.props.dispatch, this.props.userId); // don't forget to pass dispatch:। मुझे प्रेषण में पास करने की आवश्यकता क्यों है? यदि कन्वेंशन द्वारा कभी भी केवल एक ही वैश्विक स्टोर है, तो मैं सिर्फ संदर्भ क्यों नहीं करता हूं और store.dispatchजब भी मुझे आवश्यकता होती है, जैसे, जैसे loadData?
सोरेन डेबिस

10
@ SørenDebois यदि आपका ऐप केवल क्लाइंट पक्ष है जो काम करेगा। यदि यह सर्वर पर प्रदान किया गया है, storeतो आप हर अनुरोध के लिए एक अलग उदाहरण रखना चाहेंगे ताकि आप इसे पहले से परिभाषित न कर सकें।
दान अब्रामोव

3
केवल यह बताना चाहते हैं कि इस उत्तर में 139 रेखाएँ हैं, जो redux-thunk के स्रोत कोड से 9.92 गुना अधिक है, जिसमें 14 लाइनें शामिल हैं: github.com/gaearon/redux-thunk/blob/master/srcbindex.js
गाइ

447

तुम नहीं।

लेकिन ... आपको redux-saga का उपयोग करना चाहिए :)

डैन अब्रामोव का जवाब सही है, redux-thunkलेकिन मैं Redux-saga के बारे में थोड़ा और बात करूंगा जो काफी समान है लेकिन अधिक शक्तिशाली है।

इंपीरियल वीएस घोषणात्मक

  • DOM : jQuery जरूरी है / प्रतिक्रियाशील घोषणात्मक है
  • मोनाड्स : IO अत्यावश्यक है / मुक्त घोषणात्मक है
  • Redux प्रभाव : redux-thunkअनिवार्य / redux-sagaघोषणात्मक है

जब आपके हाथों में एक कांटा होता है, जैसे कि IO मोनाद या एक वादा, आप आसानी से नहीं जान सकते कि यह एक बार निष्पादित होने पर क्या करेगा। एक ठग का परीक्षण करने का एकमात्र तरीका इसे निष्पादित करना है, और डिस्पैचर (या अगर यह पूरी सामग्री के साथ इंटरैक्ट करता है ...) तो मॉक डिस्पैचर का मजाक उड़ाएं।

यदि आप मोज़ेक का उपयोग कर रहे हैं, तो आप कार्यात्मक प्रोग्रामिंग नहीं कर रहे हैं।

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

स्रोत

सगाओं (जैसा कि उन्हें लागू किया गया redux-saga) घोषणात्मक हैं और फ्री मोनाड या रिएक्ट घटकों की तरह, वे बिना किसी नकली के परीक्षण के लिए बहुत आसान हैं।

इस लेख को भी देखें :

आधुनिक एफपी में, हमें कार्यक्रम नहीं लिखना चाहिए - हमें कार्यक्रमों का वर्णन लिखना चाहिए, जिसे हम तब इच्छा-शक्ति में बदल सकते हैं, व्याख्या कर सकते हैं और व्याख्या कर सकते हैं।

(वास्तव में, Redux-saga एक हाइब्रिड की तरह है: प्रवाह अत्यावश्यक है लेकिन प्रभाव घोषणात्मक हैं)

भ्रम: क्रियाएँ / घटनाएँ / आदेश ...

फ्रंटएंड की दुनिया में बहुत भ्रम है कि CQRS / EventSourcing और Flux / Redux जैसी कुछ बैकएंड अवधारणाएं कैसे संबंधित हो सकती हैं, ज्यादातर इसलिए क्योंकि फ्लक्स में हम "एक्शन" शब्द का उपयोग करते हैं जो कभी-कभी अनिवार्य कोड ( LOAD_USER) और घटनाओं दोनों का प्रतिनिधित्व कर सकता है। USER_LOADED)। मेरा मानना ​​है कि ईवेंट-सोर्सिंग की तरह, आपको केवल ईवेंट भेजना चाहिए।

व्यवहार में सगाओं का उपयोग करना

उपयोगकर्ता प्रोफ़ाइल के लिंक के साथ एक ऐप की कल्पना करें। प्रत्येक मिडलवेयर के साथ इसे संभालने का मुहावरेदार तरीका होगा:

redux-thunk

<div onClick={e => dispatch(actions.loadUserProfile(123)}>Robert</div>

function loadUserProfile(userId) {
  return dispatch => fetch(`http://data.com/${userId}`)
    .then(res => res.json())
    .then(
      data => dispatch({ type: 'USER_PROFILE_LOADED', data }),
      err => dispatch({ type: 'USER_PROFILE_LOAD_FAILED', err })
    );
}

redux-saga

<div onClick={e => dispatch({ type: 'USER_NAME_CLICKED', payload: 123 })}>Robert</div>


function* loadUserProfileOnNameClick() {
  yield* takeLatest("USER_NAME_CLICKED", fetchUser);
}

function* fetchUser(action) {
  try {
    const userProfile = yield fetch(`http://data.com/${action.payload.userId }`)
    yield put({ type: 'USER_PROFILE_LOADED', userProfile })
  } 
  catch(err) {
    yield put({ type: 'USER_PROFILE_LOAD_FAILED', err })
  }
}

इस गाथा का अनुवाद है:

जब भी कोई उपयोगकर्ता नाम क्लिक करता है, तो उपयोगकर्ता प्रोफ़ाइल प्राप्त करता है और फिर भरी हुई प्रोफ़ाइल के साथ एक घटना भेजता है।

जैसा कि आप देख सकते हैं, के कुछ फायदे हैं redux-saga

यह takeLatestव्यक्त करने के लिए परमिट का उपयोग कि आप केवल अंतिम उपयोगकर्ता नाम के डेटा को प्राप्त करने में रुचि रखते हैं (उपयोगकर्ता के बहुत उपयोगकर्ता नाम पर बहुत तेज़ी से क्लिक करने पर समसामयिक समस्याओं को हैंडल करें)। इस तरह का सामान थ्रक्स के साथ कठिन है। takeEveryयदि आप इस व्यवहार को नहीं चाहते हैं तो आप इसका इस्तेमाल कर सकते हैं ।

आप एक्शन क्रिएटर्स को शुद्ध रखते हैं। ध्यान दें कि एक्शनक्रिएटर्स (सागों putऔर घटकों में dispatch) रखना अभी भी उपयोगी है , क्योंकि यह आपको भविष्य में एक्शन वैलिडेशन (दावे / प्रवाह / टाइपस्क्रिप्ट) जोड़ने में मदद कर सकता है।

आपका कोड बहुत अधिक परीक्षण योग्य हो जाता है क्योंकि प्रभाव घोषणात्मक होते हैं

आपको अब जैसे आरपीसी जैसी कॉल को ट्रिगर करने की आवश्यकता नहीं है actions.loadUser()। आपके यूआई को केवल यह बताने की जरूरत है कि क्या है। हम केवल आग की घटनाओं (हमेशा पिछले तनाव में!) और अब कार्रवाई नहीं करते हैं। इसका मतलब है कि आप डिकोड किए गए "बतख" या बाउंडेड कॉन्टेक्ट्स बना सकते हैं और यह कि गाथा इन मॉड्यूलर घटकों के बीच युग्मन बिंदु के रूप में कार्य कर सकती है।

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

उदाहरण के लिए एक अनंत स्क्रॉल दृश्य की कल्पना करें। CONTAINER_SCROLLEDके लिए नेतृत्व कर सकते हैं NEXT_PAGE_LOADED, लेकिन क्या यह सच है कि स्क्रॉल करने योग्य कंटेनर की जिम्मेदारी यह तय करने के लिए है कि हमें एक और पेज लोड करना चाहिए या नहीं? फिर उसे अधिक जटिल सामानों के बारे में पता होना चाहिए जैसे कि पिछले पृष्ठ को सफलतापूर्वक लोड किया गया था या नहीं या यदि पहले से ही कोई पृष्ठ है जो लोड करने की कोशिश करता है, या यदि लोड करने के लिए और अधिक आइटम नहीं बचा है? मुझे ऐसा नहीं लगता: अधिकतम पुन: प्रयोज्य के लिए स्क्रॉल करने योग्य कंटेनर को केवल यह वर्णन करना चाहिए कि इसे स्क्रॉल किया गया है। किसी पृष्ठ का लोडिंग उस स्क्रॉल का "व्यावसायिक प्रभाव" है

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

सगा समय-यात्रा की जा सकती है और जटिल प्रवाह लॉगिंग और देव-उपकरण को सक्षम बनाती है जो वर्तमान में काम कर रहे हैं। यहाँ कुछ सरल async प्रवाह लॉगिंग है जो पहले से ही लागू है:

गाथा प्रवाह लॉगिंग

decoupling

सगा न केवल रेडक्स थ्रक्स की जगह ले रहे हैं। वे बैकएंड / वितरित सिस्टम / इवेंट-सोर्सिंग से आते हैं।

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

पहली जगह में, गाथा सॉफ्टवेयर का एक टुकड़ा है जो लंबे समय तक चलने वाले लेनदेन (अंततः स्थिरता) और विभिन्न बंधे हुए संदर्भों (डोमेन संचालित डिजाइन शब्दजाल) में लेनदेन को समन्वित करने की अनुमति देता है।

फ्रंटएंड वर्ल्ड के लिए इसे सरल बनाने के लिए, विजेट 1 और विजेट 2 की कल्पना करें। जब विजेट 1 के कुछ बटन पर क्लिक किया जाता है, तो विजेट 2 पर इसका प्रभाव होना चाहिए। 2 विगेट्स को एक साथ युग्मित करने के बजाय (यानी विजेट 1 विजेट 2 को लक्षित करने वाली क्रिया को प्रेषित करता है), विजेट 1 केवल प्रेषण करता है कि इसके बटन पर क्लिक किया गया था। तब गाथा इस बटन पर क्लिक करने के लिए सुनती है और फिर विजेट 2 से अवगत होने वाली एक नई घटना का प्रसारण करके विजेट 2 को अपडेट करती है।

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

अपने Redux ऐप को कैसे तैयार करें, इस पर कुछ अच्छे लेख, जिन पर आप डिकॉक्सिंग कारणों से Redux-saga का उपयोग कर सकते हैं:

एक ठोस usecase: अधिसूचना प्रणाली

मैं चाहता हूं कि मेरे घटक इन-ऐप सूचनाओं के प्रदर्शन को ट्रिगर करने में सक्षम हों। लेकिन मैं नहीं चाहता कि मेरे घटकों को अधिसूचना प्रणाली के लिए अत्यधिक युग्मित किया जाए जिसके अपने स्वयं के व्यावसायिक नियम हैं (अधिकतम 3 सूचनाएं एक ही समय में प्रदर्शित की जाती हैं, अधिसूचना कतारबद्ध, 4 सेकंड डिस्प्ले-टाइम आदि ...)।

मैं नहीं चाहता कि मेरे JSX घटक यह तय करें कि अधिसूचना कब दिखाई जाएगी / छिप जाएगी। मैं इसे केवल एक अधिसूचना का अनुरोध करने की क्षमता देता हूं, और गाथा के अंदर के जटिल नियमों को छोड़ देता हूं। इस तरह का सामान थ्रेड्स या वादों के साथ लागू करने के लिए काफी कठिन है।

सूचनाएं

मैंने यहाँ वर्णन किया है कि यह गाथा के साथ कैसे किया जा सकता है

इसे सागा क्यों कहा जाता है?

गाथा शब्द बैकेंड की दुनिया से आता है। मैंने शुरू में एक लंबी चर्चा में यासीन (Redux-saga के लेखक) को उस शब्द से परिचित कराया ।

प्रारंभ में, उस शब्द को एक पेपर के साथ पेश किया गया था , गाथा पैटर्न को वितरित लेनदेन में अंततः स्थिरता को संभालने के लिए इस्तेमाल किया जाना था, लेकिन इसका उपयोग बैकएंड डेवलपर्स द्वारा एक व्यापक परिभाषा तक बढ़ा दिया गया है ताकि यह अब "प्रक्रिया प्रबंधक" को भी कवर करे। पैटर्न (किसी तरह मूल गाथा पैटर्न प्रक्रिया प्रबंधक का एक विशेष रूप है)।

आज, "गाथा" शब्द भ्रामक है क्योंकि यह 2 अलग-अलग चीजों का वर्णन कर सकता है। जैसा कि इसका उपयोग redux-saga में किया जाता है, यह वितरित लेनदेन को संभालने का तरीका नहीं बताता है, बल्कि आपके ऐप में कार्यों को समन्वित करने का एक तरीका है। redux-sagaभी बुलाया जा सकता था redux-process-manager

यह सभी देखें:

वैकल्पिक

यदि आपको जनरेटर का उपयोग करने का विचार पसंद नहीं है, लेकिन आप गाथा पैटर्न और इसके डिकॉउलिंग गुणों से रूचि रखते हैं, तो आप इसे redux-observable के साथ भी प्राप्त कर सकते हैं जो epicसटीक उसी पैटर्न का वर्णन करने के लिए नाम का उपयोग करता है , लेकिन RxJS के साथ। यदि आप पहले से ही Rx से परिचित हैं, तो आप घर पर सही महसूस करेंगे।

const loadUserProfileOnNameClickEpic = action$ =>
  action$.ofType('USER_NAME_CLICKED')
    .switchMap(action =>
      Observable.ajax(`http://data.com/${action.payload.userId}`)
        .map(userProfile => ({
          type: 'USER_PROFILE_LOADED',
          userProfile
        }))
        .catch(err => Observable.of({
          type: 'USER_PROFILE_LOAD_FAILED',
          err
        }))
    );

कुछ redux-saga उपयोगी संसाधन

2017 की सलाह है

  • Redux-saga को केवल उपयोग करने के लिए अधिक उपयोग न करें। परीक्षण योग्य एपीआई कॉल केवल इसके लायक नहीं है।
  • सबसे सरल मामलों के लिए अपने प्रोजेक्ट से थ्रक्स न निकालें।
  • yield put(someActionThunk)अगर यह समझ में आता है , तो फेंकता को फैलाने में संकोच न करें ।

यदि आप Redux-saga (या Redux-observable) का उपयोग करने से डरते हैं, लेकिन बस decoupling पैटर्न की आवश्यकता है, तो redux-dispatch-subscribe की जांच करें : यह श्रोता में नए प्रेषण भेजने और ट्रिगर करने के लिए सुनने की अनुमति देता है।

const unsubscribe = store.addDispatchListener(action => {
  if (action.type === 'ping') {
    store.dispatch({ type: 'pong' });
  }
});

64
यह हर बार बेहतर हो रहा है जब मैं फिर से आता हूं। इसे ब्लॉग पोस्ट में बदलने पर विचार करें :)।
रेनरअस्पेरिट

4
अच्छा लिखने के लिए धन्यवाद। हालाँकि मैं कुछ पहलुओं पर सहमत नहीं हूँ। LOAD_USER अनिवार्य कैसे है? मेरे लिए, यह केवल घोषणात्मक नहीं है - यह महान पठनीय कोड भी देता है। जैसे। "जब मैं इस बटन को दबाता हूं तो मैं ADD_ITEM करना चाहता हूं"। मैं कोड को देख सकता हूं और समझ सकता हूं कि वास्तव में क्या हो रहा है। यदि इसके बजाय इसे "BUTTON_CLICK" के प्रभाव के लिए कुछ कहा जाता है, तो मुझे यह देखना होगा।
swelet

4
अच्छा उत्तर। अब एक और विकल्प है: github.com/blesh/redux-observable
swennemen

4
@ देर से काम करने के लिए सॉरी। जब आप प्रेषण करते हैं ADD_ITEM, तो यह जरूरी है क्योंकि आप एक कार्रवाई को भेजते हैं जिसका उद्देश्य आपके स्टोर पर प्रभाव पड़ता है: आप कुछ करने के लिए कार्रवाई की अपेक्षा करते हैं। घटना-सोर्सिंग के दर्शन को घोषणात्मक होने के नाते: आप अपने अनुप्रयोगों में परिवर्तन को ट्रिगर करने के लिए कार्रवाई नहीं करते हैं, लेकिन आप अपने आवेदन में क्या हुआ है इसका वर्णन करने के लिए पिछली घटनाओं को भेजते हैं। एक घटना का प्रेषण पर्याप्त माना जाना चाहिए कि आवेदन की स्थिति बदल गई है। तथ्य यह है कि एक Redux स्टोर है जो घटना पर प्रतिक्रिया करता है एक वैकल्पिक कार्यान्वयन विवरण है
सेबस्टियन लम्बर

3
मुझे यह उत्तर पसंद नहीं है क्योंकि यह वास्तविक प्रश्न से ध्यान भटकाने के लिए है कि किसी के पास अपनी लाइब्रेरी है। यह उत्तर दो पुस्तकालयों की तुलना प्रदान करता है, जो प्रश्न का आशय नहीं था। वास्तविक प्रश्न यह पूछ रहा है कि क्या मिडलवेयर का उपयोग करना चाहिए, जिसे स्वीकृत उत्तर द्वारा समझाया गया है।
अभिनव मनचंदा

31

संक्षिप्त उत्तर : मुझे अतुल्यकालिक समस्या के लिए पूरी तरह से उचित दृष्टिकोण की तरह लगता है। एक जोड़े के साथ।

मेरे पास एक बहुत ही समान विचार था जब एक नई परियोजना पर काम कर रहे थे जो हमने अभी अपने काम पर शुरू किया था। मैं वेनिला रेडक्स के स्टोर और रेंडरिंग घटकों को एक तरह से अपडेट करने के लिए वेनिला रेडक्स के एक बड़े प्रशंसक था, जो एक रिएक्ट कंपोनेंट ट्री की हिम्मत से बाहर रहता है। dispatchअतुल्यकालिक को संभालने के लिए उस सुरुचिपूर्ण तंत्र में हुक करना मुझे अजीब लगा ।

मैंने एक ऐसी ही दृष्टिकोण के साथ जाना समाप्त कर दिया है जिसमें आपके पास एक पुस्तकालय है जिसे मैंने अपनी परियोजना से बाहर रखा है, जिसे हमने प्रतिक्रिया-रिड्यूक्स-नियंत्रक कहा है

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

  1. जिस तरह से आपने इसे लिखा है, उन डिस्पैचिंग फ़ंक्शन का स्टोर तक पहुंच नहीं है। आप अपने UI घटकों को उन सभी सूचनाओं को भेज सकते हैं जिनके पास फंक्शनिंग फ़ंक्शन की जरूरत है। लेकिन मेरा तर्क है कि यह उन यूआई घटकों को अनावश्यक रूप से प्रेषण तर्क के लिए जोड़े। और अधिक समस्या है, async निरंतरता में अद्यतन स्थिति तक पहुँचने के लिए प्रेषण कार्य के लिए कोई स्पष्ट तरीका नहीं है।
  2. डिस्पैचिंग फ़ंक्शंस में dispatchलेक्सिकल स्कोप के माध्यम से ही पहुंच है । एक बार जब यह connectकथन हाथ से निकल जाता है तो इसे फिर से भरने के लिए विकल्पों को सीमित कर देता है - और यह केवल एक updateविधि के साथ बहुत ही सुंदर है । तो अगर आप अलग मॉड्यूल में उन्हें तोड़ने के लिए उन डिस्पैचर कार्यों की रचना करने के लिए आपको कुछ प्रणाली की आवश्यकता है।

एक साथ ले लो, आपको अनुमति देने के लिए कुछ सिस्टम को रिग करना होगा dispatchऔर स्टोर को आपके प्रेषण कार्यों में इंजेक्ट किया जाएगा, साथ ही घटना के मापदंडों के साथ। मुझे इस निर्भरता इंजेक्शन के लिए तीन उचित दृष्टिकोणों का पता है:

  • redux-thunk इसे एक कार्यात्मक तरीके से करता है, उन्हें आपके थ्रो में पास करके (उन्हें गुंबद परिभाषा द्वारा बिल्कुल नहीं फेंकता है)। मैंने अन्य dispatchमिडलवेयर दृष्टिकोणों के साथ काम नहीं किया है , लेकिन मुझे लगता है कि वे मूल रूप से समान हैं।
  • प्रतिक्रिया-रिड्यूक्स-नियंत्रक एक कोरटाइन के साथ ऐसा करता है। एक बोनस के रूप में, यह आपको "चयनकर्ताओं" तक भी पहुंच प्रदान करता है, जो ऐसे कार्य हैं जिन्हें आप पहले तर्क के रूप में पारित कर सकते हैं connect, बजाय सीधे कच्चे, सामान्यीकृत स्टोर के साथ काम करने के लिए।
  • आप इसे thisविभिन्न प्रकार के संभावित तंत्रों के माध्यम से संदर्भ में इंजेक्ट करके वस्तु-उन्मुख तरीके से भी कर सकते हैं ।

अपडेट करें

मेरे साथ ऐसा होता है कि इस कॉनड्रम का हिस्सा प्रतिक्रिया-रिडक्स की एक सीमा है । connectराज्य स्नैपशॉट प्राप्त करने का पहला तर्क , लेकिन प्रेषण नहीं। दूसरा तर्क प्रेषण का है लेकिन राज्य का नहीं। निरंतर या कॉलबैक के समय अद्यतन स्थिति को देखने में सक्षम होने के लिए न तो तर्क को वर्तमान स्थिति में बंद होने वाला एक थंक मिलता है।


22

अब्रामोव का लक्ष्य - और सभी का आदर्श - बस उस स्थान पर जटिलता (और एसिंक्स कॉल) को इनकैप्सुलेट करना है जहां यह सबसे उपयुक्त है

मानक Redux डेटाफ्लो में ऐसा करने के लिए सबसे अच्छी जगह कहां है? कैसा रहेगा:

  • कम करने वाली ? बिल्कुल नहीं। बिना किसी दुष्परिणाम के उन्हें शुद्ध कार्य करने चाहिए। स्टोर को अपडेट करना गंभीर, जटिल व्यवसाय है। इसे दूषित मत करो।
  • गूंगा देखें घटक?निश्चित रूप से नहीं। उनकी एक चिंता है: प्रस्तुति और उपयोगकर्ता-सहभागिता, और जितना संभव हो उतना सरल होना चाहिए।
  • कंटेनर घटक?संभव, लेकिन उप-इष्टतम। यह समझ में आता है कि कंटेनर एक ऐसी जगह है जहां हम कुछ दृश्य संबंधी जटिलता को समाहित करते हैं और स्टोर के साथ बातचीत करते हैं, लेकिन:
    • कंटेनर को गूंगे घटकों की तुलना में अधिक जटिल होने की आवश्यकता है, लेकिन यह अभी भी एक ही जिम्मेदारी है: दृश्य और राज्य / स्टोर के बीच बाइंडिंग प्रदान करना। आपका async तर्क उससे एक अलग चिंता है।
    • एक कंटेनर में रखकर, आप अपने async तर्क को एक ही संदर्भ में, एक ही दृश्य / मार्ग के लिए लॉक कर देंगे। बुरा विचार। आदर्श रूप से यह सब पुन: प्रयोज्य है, और पूरी तरह से विघटित है।
  • S ome अन्य सेवा मॉड्यूल? बुरा विचार: आपको स्टोर पर पहुंच को इंजेक्ट करने की आवश्यकता होगी, जो एक स्थिरता / परीक्षण योग्य दुःस्वप्न है। Redux के अनाज के साथ जाने के लिए बेहतर है और केवल एपीआई / मॉडल का उपयोग करके स्टोर तक पहुंचें।
  • कार्य और मध्यवर्ग जो उनकी व्याख्या करते हैं? क्यों नहीं?! शुरुआत के लिए, यह एकमात्र बड़ा विकल्प है जिसे हमने छोड़ा है। :-) अधिक तार्किक रूप से, एक्शन सिस्टम को एग्जिक्यूटिव लॉजिक है जिसे आप कहीं से भी उपयोग कर सकते हैं। यह स्टोर तक पहुंच गया है और अधिक कार्यों को भेज सकता है। यह एक एकल जिम्मेदारी है जो अनुप्रयोग के चारों ओर नियंत्रण और डेटा के प्रवाह को व्यवस्थित करने के लिए है, और सबसे async सही में फिट बैठता है।
    • एक्शन क्रिएटर्स के बारे में क्या? क्यों नहीं, वहाँ async खुद कार्रवाई के बजाय, और Middleware में?
      • पहले और सबसे महत्वपूर्ण, रचनाकारों के पास स्टोर तक पहुंच नहीं है, जैसा कि मिडलवेयर करता है। इसका मतलब है कि आप नई आकस्मिक क्रियाओं को नहीं भेज सकते हैं, अपने एस्किंस आदि की रचना करने के लिए स्टोर से नहीं पढ़ सकते हैं।
      • इसलिए, ऐसी जगह पर जटिलता रखें जो आवश्यकता के लिए जटिल है, और बाकी सब कुछ सरल रखें। रचनाकार तब सरल, अपेक्षाकृत शुद्ध कार्य कर सकते हैं जो परीक्षण करने में आसान होते हैं।

कंटेनर घटक - क्यों नहीं? रिएक्ट में भूमिका निभाने वाले घटकों के कारण, एक कंटेनर सेवा वर्ग के रूप में कार्य कर सकता है, और इसे पहले से ही DI (सहारा) के माध्यम से एक स्टोर मिलता है। एक कंटेनर में रखकर, आप अपने async तर्क को एक ही संदर्भ में, एक ही दृश्य / मार्ग के लिए लॉक कर देंगे - ऐसा कैसे? एक घटक के कई उदाहरण हो सकते हैं। इसे प्रेजेंटेशन से डिकूप किया जा सकता है, जैसे कि रेंडर प्रॉप। मुझे लगता है कि उत्तर छोटे उदाहरणों से और भी अधिक लाभान्वित हो सकता है जो इस बात को साबित करते हैं।
एस्टुस फ्लास्क

मुझे यह जवाब पसंद है!
मारीशियो एवेन्डेनो

13

शुरुआत में पूछे जाने वाले प्रश्न का उत्तर देने के लिए:

कंटेनर घटक async एपीआई को कॉल क्यों नहीं कर सकता है, और फिर कार्रवाई को भेज सकता है?

ध्यान रखें कि वो डॉक्स Redux के लिए हैं, Redux प्लस रिएक्ट के लिए नहीं। Redux स्टोर रिएक्ट किए गए घटकों को ठीक वही करते हैं जो आप कहते हैं, लेकिन सादे मिडल dispatchऑब्जेक्ट्स को छोड़कर कोई मिडलवेयर वाला प्लेन जेन रेडक्स स्टोर तर्क स्वीकार नहीं करता है ।

मिडिलवेयर के बिना आप निश्चित रूप से अभी भी कर सकते हैं

const store = createStore(reducer);
MyAPI.doThing().then(resp => store.dispatch(...));

लेकिन यह एक ऐसा ही मामला है जहां अतुल्यकालिक लपेटा जाता है Redux द्वारा नियंत्रित करने के बजाय Redux के गया है । तो, मिडलवेयर ने एसिंक्रोनसी के लिए अनुमति देता है कि सीधे क्या पारित किया जा सकता है dispatch


उस ने कहा, आपके सुझाव की भावना मुझे मान्य है। निश्चित रूप से अन्य तरीके हैं जिनसे आप Redux + React एप्लिकेशन में एसिंक्रोनसिटी को संभाल सकते हैं।

मिडलवेयर का उपयोग करने का एक लाभ यह है कि आप एक्शन क्रिएटर्स को सामान्य रूप से बिना किसी चिंता के उपयोग करना जारी रख सकते हैं कि वे कैसे आदी हैं। उदाहरण के लिए, उपयोग करते हुए redux-thunk, आपके द्वारा लिखा गया कोड बहुत कुछ दिखेगा

function updateThing() {
  return dispatch => {
    dispatch({
      type: ActionTypes.STARTED_UPDATING
    });
    AsyncApi.getFieldValue()
      .then(result => dispatch({
        type: ActionTypes.UPDATED,
        payload: result
      }));
  }
}

const ConnectedApp = connect(
  (state) => { ...state },
  { update: updateThing }
)(App);

जो मूल से अलग नहीं दिखता है - यह सिर्फ थोड़ा सा फेरबदल है - और connectपता नहीं है कि updateThing(या होने की जरूरत है) अतुल्यकालिक है।

यदि आप वादों , वेधशालाओं , सगाओं , या पागल रिवाज और अत्यधिक घोषणात्मक एक्शन रचनाकारों का समर्थन करना चाहते हैं , तो Redux इसे बदलकर आप जो करते हैं dispatch(उर्फ, जो आप एक्शन क्रिएटर्स से लौटते हैं) कर सकते हैं। रिएक्ट घटकों (या connectकॉल) के साथ कोई मैकिंग आवश्यक नहीं है।


आप कार्रवाई पूरा होने पर अभी तक एक और घटना भेजने की सलाह देते हैं। जब आप कार्रवाई पूरी होने के बाद अलर्ट () दिखाना चाहते हैं तो यह काम नहीं करेगा। प्रतिक्रिया घटकों के अंदर वादे हालांकि काम करते हैं। मैं वर्तमान में वादे दृष्टिकोण की सिफारिश करता हूं।
कैटमफेटामाइन

8

ठीक है, चलो यह देखना शुरू करें कि मिडलवेयर पहले कैसे काम कर रहा है, जो सवाल का काफी जवाब देता है, यह स्रोत कोड है । Redux में एक pplyMiddleWare फ़ंक्शन:

function applyMiddleware() {
  for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) {
    middlewares[_key] = arguments[_key];
  }

  return function (createStore) {
    return function (reducer, preloadedState, enhancer) {
      var store = createStore(reducer, preloadedState, enhancer);
      var _dispatch = store.dispatch;
      var chain = [];

      var middlewareAPI = {
        getState: store.getState,
        dispatch: function dispatch(action) {
          return _dispatch(action);
        }
      };
      chain = middlewares.map(function (middleware) {
        return middleware(middlewareAPI);
      });
      _dispatch = compose.apply(undefined, chain)(store.dispatch);

      return _extends({}, store, {
        dispatch: _dispatch
      });
    };
  };
}

इस हिस्से में देखो, किस तरह हमारे देखने के प्रेषण एक बन समारोह

  ...
  getState: store.getState,
  dispatch: function dispatch(action) {
  return _dispatch(action);
}
  • ध्यान दें कि प्रत्येक मिडलवेयर को नामित तर्क के रूप में कार्य dispatchऔर दिए जाएंगे getState

ठीक है, यह है कि Redux के लिए सबसे अधिक इस्तेमाल किए जाने वाले बिचौलियों में से एक के रूप में Redux-thunk :

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

जैसा कि आप देख रहे हैं, यह एक क्रिया के बजाय एक फ़ंक्शन लौटाएगा, इसका मतलब है कि आप प्रतीक्षा कर सकते हैं और इसे कभी भी कॉल कर सकते हैं जैसा कि आप चाहते हैं कि यह एक फ़ंक्शन है ...

तो बिल्ली क्या है? इसे विकिपीडिया में प्रस्तुत किया गया है:

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

यह शब्द "विचार" के एक व्युत्पन्न व्युत्पन्न के रूप में उत्पन्न हुआ।

एक थंक एक फ़ंक्शन है जो इसके मूल्यांकन में देरी के लिए एक अभिव्यक्ति लपेटता है।

//calculation of 1 + 2 is immediate 
//x === 3 
let x = 1 + 2;

//calculation of 1 + 2 is delayed 
//foo can be called later to perform the calculation 
//foo is a thunk! 
let foo = () => 1 + 2;

तो देखें कि यह अवधारणा कितनी आसान है और यह आपको अपने async कार्यों को प्रबंधित करने में कैसे मदद कर सकती है ...

यह कुछ ऐसा है जो आप इसके बिना रह सकते हैं, लेकिन याद रखें कि प्रोग्रामिंग में हमेशा बेहतर, बेहतर और चीजों को करने के उचित तरीके होते हैं ...

मिडिलवेयर Redux लागू करें


1
एसओ पर पहली बार, कुछ भी नहीं पढ़ा। लेकिन बस तस्वीर को टकटकी लगाए पोस्ट पसंद आया। अद्भुत, संकेत और अनुस्मारक।
भोजेंद्र रौनियार

2

Redux-saga का उपयोग करने के लिए React-redux कार्यान्वयन में सबसे अच्छा मिडलवेयर है।

Ex: store.js

  import createSagaMiddleware from 'redux-saga';
  import { createStore, applyMiddleware } from 'redux';
  import allReducer from '../reducer/allReducer';
  import rootSaga from '../saga';

  const sagaMiddleware = createSagaMiddleware();
  const store = createStore(
     allReducer,
     applyMiddleware(sagaMiddleware)
   )

   sagaMiddleware.run(rootSaga);

 export default store;

और फिर गाथा। Js

import {takeLatest,delay} from 'redux-saga';
import {call, put, take, select} from 'redux-saga/effects';
import { push } from 'react-router-redux';
import data from './data.json';

export function* updateLesson(){
   try{
       yield put({type:'INITIAL_DATA',payload:data}) // initial data from json
       yield* takeLatest('UPDATE_DETAIL',updateDetail) // listen to your action.js 
   }
   catch(e){
      console.log("error",e)
     }
  }

export function* updateDetail(action) {
  try{
       //To write store update details
   }  
    catch(e){
       console.log("error",e)
    } 
 }

export default function* rootSaga(){
    yield [
        updateLesson()
       ]
    }

और फिर कार्रवाई। Js

 export default function updateFruit(props,fruit) {
    return (
       {
         type:"UPDATE_DETAIL",
         payload:fruit,
         props:props
       }
     )
  }

और फिर reducer.js

import {combineReducers} from 'redux';

const fetchInitialData = (state=[],action) => {
    switch(action.type){
      case "INITIAL_DATA":
          return ({type:action.type, payload:action.payload});
          break;
      }
     return state;
  }
 const updateDetailsData = (state=[],action) => {
    switch(action.type){
      case "INITIAL_DATA":
          return ({type:action.type, payload:action.payload});
          break;
      }
     return state;
  }
const allReducers =combineReducers({
   data:fetchInitialData,
   updateDetailsData
 })
export default allReducers; 

और फिर main.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './app/components/App.jsx';
import {Provider} from 'react-redux';
import store from './app/store';
import createRoutes from './app/routes';

const initialState = {};
const store = configureStore(initialState, browserHistory);

ReactDOM.render(
       <Provider store={store}>
          <App />  /*is your Component*/
       </Provider>, 
document.getElementById('app'));

यह कोशिश करो .. काम कर रहा है


3
यह किसी के लिए गंभीर चीज है जो किसी इकाई या संस्थाओं की सूची को वापस करने के लिए एपीआई एंडपॉइंट को कॉल करना चाहता है। आप सलाह देते हैं, "बस यह करो ... फिर यह, फिर यह, फिर यह दूसरी बात, फिर वह, फिर यह दूसरा सामान, फिर जारी रखना, फिर करना .."। लेकिन यार, यह FRONTEND है, हमें सिर्फ इस बात की आवश्यकता है कि हमें बैकेंड पर उपयोग किए जाने के लिए तैयार डेटा देने के लिए BACKEND पर कॉल करना होगा। इस जाने का रास्ता है, कुछ गलत है, वास्तव में कुछ गलत है और किसी को KISS आजकल आवेदन नहीं है
zameb

नमस्ते, कोशिश करें और एपीआई कॉल के लिए ब्लॉक को पकड़ें। एक बार जब एपीआई ने प्रतिक्रिया दे दी है, तो Reducer कार्रवाई प्रकारों को कॉल करें।
एसएम चिन्ना

1
@zameb आप सही हो सकते हैं, लेकिन आपकी शिकायत, फिर, Redux के साथ ही है, और जटिलता को कम करने की कोशिश करते हुए यह सब कुछ सुनती है।
जॉरिस

1

सिंक्रोनस एक्शन क्रिएटर हैं और फिर एसिंक्रोनस एक्शन क्रिएटर हैं।

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

अतुल्यकालिक एक्शन क्रिएटर्स वह है जिसमें किसी कार्रवाई को भेजने के लिए तैयार होने से पहले उसे थोड़ा समय देना होगा।

परिभाषा के अनुसार, कभी भी आपके पास एक एक्शन क्रिएटर होता है जो नेटवर्क अनुरोध करता है, यह हमेशा एक async एक्शन क्रिएटर के रूप में अर्हता प्राप्त करने वाला होता है।

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

आप इसे त्रुटि संदेश में सत्यापित कर सकते हैं जो बताता है कि हम async क्रियाओं के लिए कस्टम मिडलवेयर का उपयोग करते हैं।

तो क्या एक मिडलवेयर है और हमें Redux में async फ्लो के लिए इसकी आवश्यकता क्यों है?

Redux-thunk जैसे redux मिडलवेयर के संदर्भ में, एक मिडलवेयर हमें अतुल्यकालिक एक्शन क्रिएटर्स से निपटने में मदद करता है, क्योंकि यह कुछ ऐसा है जिसे Redux बॉक्स से बाहर नहीं कर सकता।

Redux चक्र में एकीकृत एक मिडलवेयर के साथ, हम अभी भी एक्शन क्रिएटर बुला रहे हैं, जो एक ऐसी कार्रवाई को वापस करने जा रहा है जिसे भेजा जाएगा लेकिन अब जब हम किसी एक्शन को भेजते हैं, तो इसे सीधे हमारे सभी reducers को भेजने के बजाय, हम जा रहे हैं यह कहने के लिए कि एप्लिकेशन के अंदर सभी विभिन्न मिडलवेयर के माध्यम से एक कार्रवाई भेजी जाएगी।

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

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

वहाँ बहुत सारे ओपन सोर्स मिडलवेयर हैं जिन्हें आप अपने प्रोजेक्ट में निर्भरता के रूप में स्थापित कर सकते हैं।

आप केवल ओपन सोर्स मिडलवेयर का उपयोग करने या उन्हें निर्भरता के रूप में स्थापित करने तक सीमित नहीं हैं। आप अपने स्वयं के कस्टम मिडलवेयर को लिख सकते हैं और इसका उपयोग अपने Redux स्टोर के अंदर कर सकते हैं।

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

कई अन्य प्रकार के मिडलवेयर हैं जो आपको अतुल्यकालिक एक्शन क्रिएटर्स से निपटने में भी मदद करते हैं।


1

प्रश्न का उत्तर देने के लिए:

कंटेनर घटक async एपीआई को कॉल क्यों नहीं कर सकता है, और फिर कार्रवाई को भेज सकता है?

मैं कम से कम दो कारणों से कहूंगा:

पहला कारण है चिंताओं को अलग करना, यह action creatorकॉल करने apiऔर डेटा वापस पाने का काम नहीं है , आपको अपने action creator function, action typeऔर ए के लिए दो तर्क पारित करने होंगे payload

दूसरा कारण यह है कि redux storeअनिवार्य कार्रवाई प्रकार और वैकल्पिक रूप से एक सादे वस्तु की प्रतीक्षा कर रहा है payload(लेकिन यहां आपको पेलोड भी पास करना होगा)।

कार्रवाई निर्माता नीचे की तरह एक सादा वस्तु होनी चाहिए:

function addTodo(text) {
  return {
    type: ADD_TODO,
    text
  }
}

और का काम Redux-Thunk midlewareकरने के लिए dispacheअपने का परिणाम api callउचित करने के लिए action


0

जब एक उद्यम परियोजना में काम करते हैं, तो मध्य-वेयर में कई आवश्यकताएं उपलब्ध होती हैं जैसे (गाथा) सरल अतुल्यकालिक प्रवाह में उपलब्ध नहीं हैं, नीचे कुछ हैं:

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

सूची लंबी है सिर्फ गाथा प्रलेखन में उन्नत अनुभाग की समीक्षा करें

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