2016-04-02 6 views
14

में निर्भरता इंजेक्शन मैं वर्तमान में एक शिक्षार्थी रिएक्ट/रेडक्स एप्लिकेशन का निर्माण कर रहा हूं और मैं सेवाओं के लिए निर्भरता इंजेक्शन कैसे करें, इसके बारे में अपने सिर को लपेट नहीं सकता।रेडक्स एक्शन निर्माता

अधिक विशिष्ट होने के लिए: मेरे पास BluetoothService (जो तृतीय पक्ष लाइब्रेरी को सारणीबद्ध करता है) ब्लूटूथ के माध्यम से अन्य उपकरणों को स्कैन और कनेक्ट करने के लिए है। यह सेवा कार्रवाई रचनाकारों द्वारा उपयोग किया जाता है, कुछ इस तरह:

const bluetoothService = require('./blueToothService') 
function addDevice(device) { 
    return { type: 'ADD_DEVICE', device } 
} 

function startDeviceScan() { 
    return function (dispatch) { 
     // The Service invokes the given callback for each found device 
     bluetoothService.startDeviceSearch((device) => { 
      dispatch(addDevice(device)); 
     }); 
    } 
} 
module.exports = { addDevice, startDeviceScan }; 

(मैं thunk-मिडलवेयर उपयोग कर रहा हूँ)

मेरे समस्या फिर भी है:

deviceActionCreators.js: सेवा-निर्माता में सेवा को इंजेक्ट कैसे करें?

मैं नहीं चाहता कि हार्ड-कोडित require (या import ईएस 6 में) क्योंकि मुझे नहीं लगता कि यह एक अच्छा पैटर्न है - परीक्षण को इतना कठिन बनाने के अलावा। मैं अपने वर्क स्टेशन (जिसमें ब्लूटूथ नहीं है) पर ऐप का परीक्षण करते समय एक नकली सेवा का उपयोग करने में सक्षम होना चाहता हूं - इसलिए पर्यावरण के आधार पर मैं अपने एक्शन-निर्माता के अंदर इंजेक्शन वाले एक ही इंटरफ़ेस के साथ एक और सेवा चाहता हूं। एक स्थिर आयात का उपयोग करने के साथ यह संभव नहीं है।

मैंने पहले से ही विधि के लिए ब्लूटूथ सेवा को पैरामीटर बनाने की कोशिश की है (startDeviceScan(bluetoothService){}) - प्रभावी ढंग से विधि को शुद्ध बनाते हैं - लेकिन यह समस्या को कार्रवाई का उपयोग करके कंटेनर में ले जाता है। प्रत्येक कंटेनर को तब सेवा के बारे में जानना होगा और इसके कार्यान्वयन को कार्यान्वित करना होगा (उदाहरण के लिए प्रोप के माध्यम से)। प्लस जब मैं किसी अन्य क्रिया के भीतर से कार्रवाई का उपयोग करना चाहता हूं तो मैं एक ही समस्या के साथ फिर से समाप्त होता हूं।

लक्ष्य: मैं बूटस्ट्रैपिंग समय पर निर्णय लेना चाहता हूं जो मेरे ऐप में उपयोग करने के लिए लागू है। क्या ऐसा करने के लिए कोई अच्छा तरीका या सर्वोत्तम अभ्यास है?

उत्तर

8

आप एक redux middleware कि एक async कार्रवाई का जवाब देंगे उपयोग कर सकते हैं।इस तरह आप जो कुछ भी सेवा इंजेक्षन या नकली आप एक ही स्थान में की जरूरत है सकते हैं, और एप्लिकेशन को किसी भी एपीआई कार्यान्वयन विवरण से मुक्त हो जाएगा:

// bluetoothAPI Middleware 
import bluetoothService from 'bluetoothService'; 

export const DEVICE_SCAN = Symbol('DEVICE_SCAN'); // the symbol marks an action as belonging to this api 

// actions creation helper for the middleware 
const createAction = (type, payload) => ({ 
    type, 
    payload 
}); 

// This is the export that will be used in the applyMiddleware method 
export default store => next => action => { 
    const blueToothAPI = action[DEVICE_SCAN]; 

    if(blueToothAPI === undefined) { 
     return next(action); 
    } 

    const [ scanDeviceRequest, scanDeviceSuccess, scanDeviceFailure ] = blueToothAPI.actionTypes; 

    next(createAction(scanDeviceRequest)); // optional - use for waiting indication, such as spinner 

    return new Promise((resolve, reject) => // instead of promise you can do next(createAction(scanDeviceSuccess, device) in the success callback of the original method 
     bluetoothService.startDeviceSearch((device) => resolve(device), (error) = reject(error)) // I assume that you have a fail callback as well 
     .then((device) => next(createAction(scanDeviceSuccess, device))) // on success action dispatch 
     .catch((error) => next(createAction(scanDeviceFailure, error))); // on error action dispatch 
}; 

// Async Action Creator 
export const startDeviceScan = (actionTypes) => ({ 
    [DEVICE_SCAN]: { 
     actionTypes 
    } 
}); 

// ACTION_TYPES 
export const SCAN_DEVICE_REQUEST = 'SCAN_DEVICE_REQUEST'; 
export const SCAN_DEVICE_SUCCESS = 'SCAN_DEVICE_SUCCESS'; 
export const SCAN_DEVICE_FAILURE = 'SCAN_DEVICE_FAILURE'; 

// Action Creators - the actions will be created by the middleware, so no need for regular action creators 

// Applying the bluetoothAPI middleware to the store 
import { createStore, combineReducers, applyMiddleware } from 'redux' 
import bluetoothAPI from './bluetoothAPI'; 

const store = createStore(
    reducers, 
    applyMiddleware(bluetoothAPI); 
); 

// Usage 
import { SCAN_DEVICE_REQUEST, SCAN_DEVICE_SUCCESS, SCAN_DEVICE_FAILURE } from 'ACTION_TYPES'; 

dispatch(startDeviceScan([SCAN_DEVICE_REQUEST, SCAN_DEVICE_SUCCESS, SCAN_DEVICE_FAILURE])); 

आप कार्यों के प्रकार पर किया जाएगा साथ startDeviceScan async कार्रवाई प्रेषण, प्रासंगिक कार्यों के निर्माण में प्रयोग किया जाता है। Middleware प्रतीक DEVICE_SCAN द्वारा कार्रवाई की पहचान करता है। यदि कार्रवाई में प्रतीक नहीं है, तो यह इसे वापस स्टोर (अगले midware/reducers) पर भेजता है।

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

real world redux middle example पर भी देखें।

+0

उस विस्तृत उत्तर के लिए बहुत बहुत धन्यवाद। मैंने पहले मिडलवेयर के बारे में पढ़ा लेकिन अभी तक इसे पूरी तरह से समझ नहीं पाया। क्या आप कहेंगे कि प्रतिक्रिया-रेडक्स ऐप में सेवाओं का उपयोग करने के लिए यह मानक या सर्वोत्तम अभ्यास है? – Charminbear

+0

आपका स्वागत है - मैं इसके साथ भी संघर्ष कर रहा हूं :) –

+0

धन्यवाद! इसका उपयोग करके मैं रेडक्स और रेडक्स-थंक के साथ निर्बाध रूप से काम कर कोणीय निर्भरता इंजेक्शन प्राप्त करने में सक्षम था, जो कोणीय से हमारे संक्रमण को अधिक चिकनी प्रतिक्रिया देने में सक्षम बनाता है: https://gist.github.com/jasononeil/e835aeb2ce7b9f099616ef2098caac1d –

2

क्या आप अपने एक्शन निर्माता को अपनी सेवा में लपेट सकते हैं?

export function actionCreatorsService(bluetoothService) { 
    function addDevice(device) { 
     return { type: 'ADD_DEVICE', device } 
    } 

    function startDeviceScan() { 
     return function (dispatch) { 
     // The Service invokes the given callback for each found device 
     bluetoothService.startDeviceSearch((device) => { 
      dispatch(addDevice(device)); 
     }); 
     } 
    } 

    return { 
     addDevice, 
     startDeviceScan 
    }; 
} 

अब, इस सेवा के किसी भी ग्राहक को ब्लूटूथ सेवा का एक उदाहरण प्रदान करने की आवश्यकता होगी।

const bluetoothService = require('./actual/bluetooth/service'); 
const actionCreators = require('./actionCreators')(bluetoothService); 

और अपने परीक्षण में: अपने वास्तविक स्रोत कोड में

const mockBluetoothService = require('./mock/bluetooth/service'); 
const actionCreators = require('./actionCreators')(mockBluetoothService); 

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

const actionCreators = require('./actionCreators').actionCreators; 

और अपने परीक्षण कोड इस प्रकार दिखाई देंगे:

const actionCreators = require('./actionCreators').mockActionCreators; 
+0

त्वरित जवाब के लिए धन्यवाद। डी के साथ एक सेवा के रूप में पूरे निर्माता को लपेटना स्वयं में एक अच्छा समाधान है - लेकिन यह मुझे उसी समस्या के साथ छोड़ देता है जब मैंने इसे फ़ंक्शन में DI के रूप में करने की कोशिश की: क्रिया-निर्माता का उपयोग करने वाले कंटेनर (या ग्राहक) को यह तय करना होगा कि कौन सा उपयोग करने के लिए सेवा। लेकिन मैं पूरे ऐप को बूटस्ट्रैप करते समय पहले से ही यह निर्णय लेना चाहता हूं, इसलिए शायद पहले से ही 'main.js'-file में। क्या आप संपत्तियों के आस-पास उपयोग करने के लिए सेवा पास करेंगे? – Charminbear

+0

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

13

रिएक्ट-थंक withExtraArgument का उपयोग करके एक अबाध वस्तु को गुजरने का समर्थन करता है। आप इस का उपयोग कर सकते हैं एक सेवा वस्तु, जैसे निर्भरता सुई:

function startDeviceScan() { 
    return function (dispatch, getstate, services) { 
     // ... 
     services.bluetoothService.startDeviceSearch((device) => { 
      dispatch(addDevice(device)); 
     }); 
    } 
} 

यह एक निर्भरता का उपयोग कर के रूप में के रूप में औपचारिक नहीं है: एक तीसरा तर्क के रूप में अपने thunk को

const bluetoothService = require('./blueToothService'); 

const services = { 
    bluetoothService: bluetoothService 
}; 

let store = createStore(reducers, {}, 
    applyMiddleware(thunk.withExtraArgument(services)) 
); 

फिर सेवाएं हैं उपलब्ध Angular2 में इंजेक्शन सजावट या थंक्स को सेवाएं पास करने के लिए एक अलग रेडक्स मिडलवेयर परत बनाना --- यह सिर्फ एक "कुछ वस्तु" है जो बदसूरत है --- लेकिन दूसरी ओर यह लागू करने के लिए काफी सरल है।

1

मैंने उस उद्देश्य के लिए redux-bubble-di नामक एक निर्भरता-इंजेक्शनिंग मिडलवेयर बनाया। इसका उपयोग एक्शन क्रिएटर में निर्भरता की मनमानी संख्या को इंजेक्ट करने के लिए किया जा सकता है।

आप इसे npm install --save redux-bubble-di या download द्वारा इंस्टॉल कर सकते हैं।

redux-बुलबुला-डी का उपयोग कर अपने उदाहरण इस प्रकार दिखाई देगा:

//import { DiContainer } from "bubble-di"; 
const { DiContainer } = require("bubble-di"); 
//import { createStore, applyMiddleware } from "redux"; 
const { createStore, applyMiddleware } = require("redux"); 
//import reduxBubbleDi from "redux-bubble-di"; 
const reduxBubbleDi = require("redux-bubble-di").default; 

const bluetoothService = require('./blueToothService'); 

DiContainer.setContainer(new DiContainer()); 
DiContainer.getContainer().registerInstance("bluetoothService", bluetoothService); 

const store = createStore(
    state => state, 
    undefined, 
    applyMiddleware(reduxBubbleDi(DiContainer.getContainer())), 
); 

const startDeviceScan = { 
    bubble: (dispatch, bluetoothService) => { 
     bluetoothService.startDeviceSearch((device) => { 
      dispatch(addDevice(device)); 
     }); 
    }, 
    dependencies: ["bluetoothService"], 
}; 

// ... 

store.dispatch(startDeviceScan); 
संबंधित मुद्दे