Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/392.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript redux操作创建者中的依赖项注入_Javascript_Reactjs_Dependency Injection_Redux - Fatal编程技术网

Javascript redux操作创建者中的依赖项注入

Javascript redux操作创建者中的依赖项注入,javascript,reactjs,dependency-injection,redux,Javascript,Reactjs,Dependency Injection,Redux,我目前正在构建一个learner React/Redux应用程序,我不知道如何为服务进行依赖注入 更具体地说:我有一个BluetoothService(它抽象了一个第三方库),可以通过蓝牙扫描并连接到其他设备。此服务由动作创建者使用,如下所示: const actionCreators = require('./actionCreators').actionCreators; const actionCreators = require('./actionCreators').mockActi

我目前正在构建一个learner React/Redux应用程序,我不知道如何为服务进行依赖注入

更具体地说:我有一个
BluetoothService
(它抽象了一个第三方库),可以通过蓝牙扫描并连接到其他设备。此服务由动作创建者使用,如下所示:

const actionCreators = require('./actionCreators').actionCreators;
const actionCreators = require('./actionCreators').mockActionCreators;
//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);
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 };
(我正在使用thunk中间件)

然而,我的问题是:如何将服务本身注入到动作创建者中

我不希望在ES6中硬编码的
要求
(或
导入
),因为我认为这不是一个好的模式——而且会使测试变得更加困难。我还希望能够在我的工作站(没有蓝牙)上测试应用程序时使用模拟服务——因此,根据环境的不同,我希望在action creator中注入另一个具有相同接口的服务。使用静态导入根本不可能做到这一点

我已经尝试将bluetoothService作为方法本身的参数(
startDeviceScan(bluetoothService){}
)——有效地使方法本身变得纯粹——但这只是将问题转移到使用该操作的容器上。然后,每个容器都必须了解服务,并注入服务的实现(例如通过道具)。 另外,当我想在另一个动作中使用该动作时,我又会遇到同样的问题

目标: 我想决定在我的应用程序中使用哪个实现的引导时间。
有什么好方法或最佳实践可以做到这一点吗?

你能将你的动作创作者包装到他们自己的服务中吗

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
   };
}
现在,此服务的任何客户端都需要提供bluetoothService的实例。在实际的src代码中:

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;
//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);
您的测试代码可能如下所示:

const actionCreators = require('./actionCreators').actionCreators;
const actionCreators = require('./actionCreators').mockActionCreators;
//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);
您可以使用将响应异步操作的。通过这种方式,您可以在一个地方注入所需的任何服务或模拟,应用程序将不包含任何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]));
您可以调度startDeviceScan异步操作,以及将用于创建相关操作的操作类型。中间件通过符号设备\u扫描识别动作。如果操作不包含符号,它会将其发回存储(下一个中间件/还原器)

如果符号设备_扫描存在,中间件将提取操作类型,创建并分派启动操作(例如,对于加载微调器),发出异步请求,然后创建并分派成功或失败操作


另请看。

React thunk支持使用将任意对象传递给thunk。您可以使用此依赖项注入服务对象,例如:

const bluetoothService=require('./bluetoothService');
常数服务={
蓝牙服务:蓝牙服务
};
let store=createStore(reducer,{},
applyMiddleware(thunk.withExtraArgument(服务))
);
然后这些服务作为第三个参数可供thunk使用:

函数startDeviceScan(){
返回函数(分派、获取状态、服务){
// ...
services.bluetoothService.startDeviceSearch((设备)=>{
调度(添加设备(设备));
});
}
}

这不像在Angular2中使用依赖注入装饰器或创建单独的Redux中间件层来将服务传递给thunks那样正式——它只是一个丑陋的“任意对象”——但另一方面,它的实现相当简单。

我创建了一个依赖注入中间件,正是为了这个目的而调用的。它可以用来向动作创建者注入任意数量的依赖项

您可以通过
npm安装--save redux bubble di
或它来安装它

您使用redux bubble di的示例如下所示:

const actionCreators = require('./actionCreators').actionCreators;
const actionCreators = require('./actionCreators').mockActionCreators;
//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);

谢谢你的快速回答。用DI将整个creator包装成一个服务本身就是一个很好的解决方案——但这给我留下了与我在函数中尝试将其作为DI时相同的问题:使用action creator的容器(或客户端)必须决定使用哪种服务。但是我想在引导整个应用程序之前就做出这个决定,所以可能已经在
main.js
-文件中了。然后你会将服务传递到属性中使用吗?我还没有使用JS的DI库(除了Angular),但可能值得研究一些专门为此构建的库。我见过的其他解决方案是使用环境变量来确定导出什么(即,如果设置了某个环境变量,bluetoothService模块可能会导出模拟服务)。非常感谢您给出的详细答案。我以前读过中间件,但直到现在才完全理解它。你认为这是在react redux应用程序中使用服务的标准或最佳实践吗?欢迎-我也在努力:)谢谢!使用这一点,我能够让角度依赖注入与redux和redux thunk无缝地工作,这将使我们从角度到反应更加平滑:很高兴它有帮助:)我喜欢角度中间件,特别是关于注入器的想法。我建议您不要只使用
typeof action==“function”
作为中间件检查。如果您使用其他方法,它们也会被注释。向函数中添加一个简单类型-
action.type='whatever'
,并检查该类型。这看起来不像DI,更像是服务位置,一种反模式。@如果
startDeviceScan,它就是服务位置<