Cha*_*ear 20 javascript dependency-injection reactjs redux
我目前正在构建一个学习者React/Redux应用程序,我无法解决如何为服务进行依赖注入.
更具体一点:我有一个BluetoothService
(抽象第三方库)扫描并通过蓝牙连接到其他设备.该服务被动作创建者利用,如下所示:
deviceActionCreators.js:
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 };
Run Code Online (Sandbox Code Playgroud)
(我正在使用thunk-middleware)
我的问题是:如何将服务本身注入动作创建者?
我不希望硬编码require
(或import
在ES6中),因为我认为这不是一个好的模式 - 除了使测试更加困难之外.我还希望能够在我的工作站(没有蓝牙)上测试应用程序时使用模拟服务 - 所以根据环境我希望在我的action-creator中注入相同界面的其他服务.使用静态导入是不可能的.
我已经尝试将bluetoothService作为Method本身的一个参数(startDeviceScan(bluetoothService){}
) - 有效地使方法本身是纯粹的 - 但这只是使用动作将问题移动到容器.每个容器都必须知道该服务然后获得它的实现(例如通过props).另外,当我想在另一个动作中使用动作时,我又会遇到同样的问题.
目标:我想决定在我的应用程序中使用哪些实现的自举时间.这样做是否有好方法或最佳实践?
mik*_*dge 19
React-thunk支持使用将任意对象传递给thunk withExtraArgument
.您可以使用它来依赖注入服务对象,例如:
const bluetoothService = require('./blueToothService');
const services = {
bluetoothService: bluetoothService
};
let store = createStore(reducers, {},
applyMiddleware(thunk.withExtraArgument(services))
);
Run Code Online (Sandbox Code Playgroud)
然后作为第三个参数,您的thunk可以使用这些服务:
function startDeviceScan() {
return function (dispatch, getstate, services) {
// ...
services.bluetoothService.startDeviceSearch((device) => {
dispatch(addDevice(device));
});
}
}
Run Code Online (Sandbox Code Playgroud)
这不像在Angular2中使用依赖注入装饰器或创建一个单独的Redux中间件层来将服务传递给thunks那样正式 - 它只是一个有点难看的"任何对象" - 但另一方面它是实施起来相当简单.
Ori*_*ori 11
您可以使用将响应异步操作的redux中间件.通过这种方式,您可以在一个地方注入您需要的任何服务或模拟,并且应用程序将没有任何api实现细节:
// 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]));
Run Code Online (Sandbox Code Playgroud)
您将使用将在创建相关操作时使用的操作类型调度startDeviceScan异步操作.中间件通过符号DEVICE_SCAN识别动作.如果操作不包含该符号,则会将其发送回商店(下一个中间件/ reducer).
如果符号DEVICE_SCAN存在,则中间件提取操作类型,创建和调度启动操作(例如,对于加载微调器),发出异步请求,然后创建并分派成功或失败操作.
另外看看现实世界的redux中间例子.
归档时间: |
|
查看次数: |
6280 次 |
最近记录: |