直接调用 store.dispatch() 的替代方法

Alternative to direct calling of store.dispatch()

由于在最新的 redux-observable (0.17) 中直接调用 store.dispatch() 已弃用,我想知道如果我需要从我的 redux 应用程序外部调度操作,还有什么替代方法。

示例:

假设我有这个函数可以初始化本机模块并设置本机处理程序。

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)
        })

正确的解决方法是什么?

谢谢!

当使用 RxJS 时,理想的是组合流。所以在这种情况下,我们需要一些如何创建 "PrintingManagerNewPrinterConnected" 事件流,然后我们可以将每个事件映射到它们自己的 PRINTER_MANAGER_NEW_PRINTER_CONNECTED action.a

让我们先学习如何完全自定义。

自定义 Observables

创建您自己的自定义 Observables 与创建 Promise 非常相似。假设你有世界上最简单的 Promise,它立即解析为数字 1

const items = new Promise(resolve => {
  resolve(1);
});

等效的 Observable 看起来超级相似

const items = new Observable(observer => {
  observer.next(1);
  observer.complete();
});

在视觉上,主要区别在于我们没有传递 (resolve, reject) 回调,而是获得了一个观察者,因为有下一个、错误和完成。

语义上,Observables 可以通过多次调用 observer.next 来表示多个值,直到它们调用 observer.complete() 来表示流的结束;这与仅表示单个值的 Promises 形成对比。

默认情况下,Observables 也是惰性和同步的,而 Promises 总是急切的和异步的。

既然我们已经有了这样的理解,我们想接受它并包装您的 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 以便我们自己清理并且不会泄漏内存。

为此,我们需要 return 在我们的自定义 Observable 中进行订阅。此上下文中的订阅是一个 object,上面有一个 unsubscribe() 方法,当订阅者取消订阅、触发错误或您的可观察对象完成时,将自动调用该方法。这是你清理的机会。

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);
    }
  };
});

因为 return 一个 object 有点冗长 RxJS 支持一个 shorthand 你只是 return 一个函数本身将被视为 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));

Observable.fromEvent

虽然 NativeEventEmitter 是 react-native 的东西,但它跟在 node-style EventEmitter interface and RxJS already comes with a utility helper to create an Observable from them to save you the effort. It's called fromEvent 之后,位于 Observable.fromEventimport { 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 中,这样我们就不会创建 NativeEventEmitterprintingModule.initialize() 直到有人真正订阅(保持懒惰)。这对您来说可能是必要的,也可能不是必要的,我不知道 PrintingManager 的作用或行为方式。例如可能只需要创建一个发射器并预先初始化模块。

const printingModule = NativeModules.PrintingManager;
const printerEmitter = new NativeEventEmitter(printingModule);
printingModule.initialize();

const fromPrinterEvent = (eventName) =>
  Observable.fromEvent(printerEmitter, eventName);

所以请记住,我只是在展示模式而不知道 PrintingManager 等的作用。

在redux-observable

内使用

在 redux-observable 中使用它,您的史诗现在与您使用任何其他 Observable 一样。所以我们希望将它的值映射到操作和 mergeMap、switchMap、concatMap 或 exhaustMap 到我们的 top-level 流中。

像这样:

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 模式。