由于最新的redux-observable(0.17)是不推荐直接调用store.dispatch(),我想知道如果我需要从redux app外部调度动作,还有什么选择。
示例:
假设我有这个函数初始化本机模块并设置本机处理程序。
const configure = (dispatch) => {
const printingModule = NativeModules.PrintingManager
const eventEmitter = new NativeEventEmitter(printingModule)
eventEmitter.addListener(
"PrintingManagerNewPrinterConnected",
(payload) => dispatch({
type: PRINTER_MANAGER_NEW_PRINTER_CONNECTED,
payload: {
macAddress: payload[2],
connectionType: payload[3],
},
}))
printingModule.initialize()
}
我通常做的是在APP_STARTUP_FINISHED之后我会从observable调用这个函数:
const appStatePrepared = (action$: Object, { dispatch }) =>
action$.ofType(APP_STATE_PREPARED)
.switchMap(() => {
configurePrinters(dispatch)
})
对此有什么正确的解决方案?
谢谢!
答案 0 :(得分:5)
使用RxJS时,理想的是组合流。因此,在这种情况下,我们需要了解如何创建"PrintingManagerNewPrinterConnected"
事件流,然后我们可以将每个事件映射到他们自己的PRINTER_MANAGER_NEW_PRINTER_CONNECTED
action.a
让我们首先学习如何完全自定义。
创建自己的自定义Observable与创建Promise非常相似。所以说你拥有世界上最简单的Promise,它会立即解析为1号
const items = new Promise(resolve => {
resolve(1);
});
等效的Observable看起来非常相似
const items = new Observable(observer => {
observer.next(1);
observer.complete();
});
从视觉上看,主要区别在于我们没有传递(resolve, reject)
个回调,而是给了一个观察者,因为有下一个,错误和完整。
从语义上讲,Observable可以通过调用observer.next
多次代表多个值,直到他们调用observer.complete()
来表示流的结尾;这与Promises形成对比,Promise只代表单一值。
默认情况下,Observable也是惰性和同步的,而Promise总是渴望和异步。
现在我们已经了解了这一点,并希望将NativeEventEmitter
API包含在使用addEventListener
。
const configurePrinters = () => {
return new Observable(observer => {
const printingModule = NativeModules.PrintingManager;
const eventEmitter = new NativeEventEmitter(printingModule);
eventEmitter.addListener(
'PrintingManagerNewPrinterConnected',
(payload) => observer.next(payload)
);
printingModule.initialize();
});
};
configurePrinters()
.subscribe(payload => console.log(payload));
这很有效,而且非常简单,但它有一个问题:我们应该在取消订阅时调用removeListener
,以便我们自行清理并且不会泄漏内存。
为此,我们需要在自定义Observable中返回订阅。此上下文中的订阅是一个对象,其上有一个unsubscribe()
方法,当订阅者取消订阅,触发错误或您的observable完成时,将自动调用该方法。这是你清理的机会。
const items = new Observable(observer => {
let i = 0;
const timer = setInterval(() => {
observer.next(i++);
}, 1000);
// return a subscription that has our timer cleanup logic
return {
unsubscribe: () => {
clearInterval(timer);
}
};
});
因为返回一个对象有点冗长,RxJS支持一个简写,你只需返回一个本身将被视为unsubscribe
方法的函数。
const items = new Observable(observer => {
let i = 0;
const timer = setInterval(() => {
observer.next(i++);
}, 1000);
// return an "unsubscribe" function that has our timer cleanup logic
return () => {
clearInterval(timer);
};
});
现在我们可以将它应用于我们的示例,我们希望在调用取消订阅拆解函数时删除我们的侦听器。
const configurePrinters = () => {
return new Observable(observer => {
const printingModule = NativeModules.PrintingManager;
const eventEmitter = new NativeEventEmitter(printingModule);
const listener = (payload) => observer.next(payload);
eventEmitter.addListener(
'PrintingManagerNewPrinterConnected',
listener
);
printingModule.initialize();
return () => eventEmitter.removeListener(
'PrintingManagerNewPrinterConnected',
listener
);
});
};
现在让我们把它变成一个可重复使用的效用函数
const fromPrinterEvent = (eventName) => {
return new Observable(observer => {
const printingModule = NativeModules.PrintingManager;
const eventEmitter = new NativeEventEmitter(printingModule);
const listener = (payload) => observer.next(payload);
eventEmitter.addListener(eventName, listener);
printingModule.initialize();
return () => eventEmitter.removeListener(eventName, listener);
});
};
fromPrinterEvent('PrintingManagerNewPrinterConnected')
.subscribe(payload => console.log(payload));
虽然NativeEventEmitter
是反应原生的东西,但它跟在node-style EventEmitter interface之后,RxJS已经附带了一个实用程序助手,可以从它们创建一个Observable来节省你的工作量。 It's called fromEvent
,位于Observable.fromEvent
或import { fromEvent } from 'rxjs/observables/fromEvent'
。
const fromPrinterEvent = (eventName) => {
return Observable.defer(() => {
const printingModule = NativeModules.PrintingManager;
const eventEmitter = new NativeEventEmitter(printingModule);
printingModule.initialize();
return Observable.fromEvent(eventEmitter, eventName);
});
};
此处我还将其包装在Observable.defer
中,以便我们不会创建NativeEventEmitter
或printingModule.initialize()
,直到有人真正订阅(保持懒惰)。这对您来说可能是必要的,也可能不是,我不知道PrintingManager的作用或行为方式。例如可能需要只创建一个发射器并预先初始化模块。
const printingModule = NativeModules.PrintingManager;
const printerEmitter = new NativeEventEmitter(printingModule);
printingModule.initialize();
const fromPrinterEvent = (eventName) =>
Observable.fromEvent(printerEmitter, eventName);
所以请记住,我只是在不知道PrintingManager等所做的事情的情况下展示模式。
要在redux-observable中使用它,你的史诗现在与你使用任何其他Observable相同。因此,我们希望将值中的值映射到操作和 mergeMap,switchMap,concatMap或exhaustMap进入我们的顶级流。
这样的事情:
const appStatePrepared = action$ =>
action$.ofType(APP_STATE_PREPARED)
.switchMap(() =>
fromPrinterEvent('PrintingManagerNewPrinterConnected')
.map(payload => ({
type: PRINTER_MANAGER_NEW_PRINTER_CONNECTED,
payload: {
macAddress: payload[2],
connectionType: payload[3],
}
}))
);
请记住,许多流(包括我们的自定义fromPrinterEvent('PrintingManagerNewPrinterConnected')
)将永远消失,直到您取消订阅它们为止。因此,如果您只想要一个,请使用.take(1)
。如果您想在接收其他操作时取消订阅,请使用.takeUntil(action$.ofType(WHATEVER))
等正常的RxJS模式。