条件为真时取消订阅Redux商店?

时间:2016-06-29 02:52:29

标签: javascript redux

我正在使用the suggestion from @gaearon在我的redux商店上设置一个监听器。我正在使用这种格式:

function observeStore(store, select, onChange) {
  let currentState;

  if (!Function.prototype.isPrototypeOf(select)) {
    select = (state) => state;
  }

  function handleChange() {
    let nextState = select(store.getState());
    if (nextState !== currentState) {
      currentState = nextState;
      onChange(currentState);
    }
  }

  let unsubscribe = store.subscribe(handleChange);
  handleChange();
  return unsubscribe;
}

我在onEnter路由器的react-router处理程序中使用它:

Entity.onEnter = function makeFetchEntity(store) {
  return function fetchEntity(nextState, replace, callback) {
    const disposeRouteHandler = observeStore(store, null, (state) => {
      const conditions = [
        isLoaded(state.thing1),
        isLoaded(state.thing2),
        isLoaded(state.thing3),
      ];

      if (conditions.every((test) => !!test) {
        callback(); // allow react-router to complete routing
        // I'm done: how do I dispose the store subscription???
      }
    });

    store.dispatch(
      entities.getOrCreate({
        entitiesState: store.getState().entities,
        nextState,
      })
    );
  };
};

基本上,当操作完成调度(异步)时,这有助于控制路由器的进程。

我的问题是我无法确定在哪里拨打disposeRouteHandler()。如果我在定义之后立即调用它,我的onChange函数永远不会有机会做到这一点,我不能把它放在onChange函数中,因为它还没有定义。

在我看来是鸡蛋问题。非常感谢任何帮助/指导/见解。

2 个答案:

答案 0 :(得分:1)

怎么样:

Entity.onEnter = function makeFetchEntity(store) {
  return function fetchEntity(nextState, replace, callback) {
    let shouldDispose = false;
    const disposeRouteHandler = observeStore(store, null, (state) => {
      const conditions = [
        isLoaded(state.thing1),
        isLoaded(state.thing2),
        isLoaded(state.thing3),
      ];

      if (conditions.every((test) => !!test) {
        callback(); // allow react-router to complete routing
        if (disposeRouteHandler) {
          disposeRouteHandler();
        } else {
          shouldDispose = true;
        }
      }
    });
    if (shouldDispose) {
      disposeRouteHandler();
    }

    store.dispatch(
      entities.getOrCreate({
        entitiesState: store.getState().entities,
        nextState,
      })
    );
  };
};

尽管使用observable模式会导致一些买入,但您可以使用普通的js代码解决任何困难。或者,您可以修改您的observable以更好地满足您的需求。 例如:

function observeStore(store, select, onChange) {
  let currentState, unsubscribe;

  if (!Function.prototype.isPrototypeOf(select)) {
    select = (state) => state;
  }

  function handleChange() {
    let nextState = select(store.getState());
    if (nextState !== currentState) {
      currentState = nextState;
      onChange(currentState, unsubscribe);
    }
  }

  unsubscribe = store.subscribe(handleChange);
  handleChange();
  return unsubscribe;
}

Entity.onEnter = function makeFetchEntity(store) {
  return function fetchEntity(nextState, replace, callback) {
    const disposeRouteHandler = observeStore(store, null, (state, disposeRouteHandler) => {
      const conditions = [
        isLoaded(state.thing1),
        isLoaded(state.thing2),
        isLoaded(state.thing3),
      ];

      if (conditions.every((test) => !!test) {
        callback(); // allow react-router to complete routing
        disposeRouteHandler();
      }
    }

    store.dispatch(
      entities.getOrCreate({
        entitiesState: store.getState().entities,
        nextState,
      })
    );
  };
};

它确实为onChange添加了一个奇怪的参数,但它只是众多方法中的一种。

核心问题是handleChange在没有任何变化的情况下立即同步调用,以后再异步调用。它被称为Zalgo

答案 1 :(得分:0)

受到@DDS建议的启发,我对@gaearon's comment中提到的其他模式提出了以下修改:

export function toObservable(store) {
  return {
    subscribe({ onNext }) {
      let dispose = this.dispose = store.subscribe(() => {
        onNext.bind(this)(store.getState())
      });

      onNext.bind(this)(store.getState());

      return { dispose };
    },

    dispose: function() {},
  }
}

这允许我调用:

Entity.onEnter = function makeFetchEntity(store) {
  return function fetchEntity(nextState, replace, callback) {
    toObservable(store).subscribe({
      onNext: function onNext(state) {
        const conditions = [/* many conditions */];

        if (conditions.every((test) => !!test) {
          callback(); // allow react-router to complete routing
          this.dispose(); // remove the store subscription
        }
      },
    });

    store.dispatch(/* action */);
  };
};

关键的区别在于我正在为onNext传递常规函数,以免干扰bind(this)中的toObservable;我无法弄清楚如何强制绑定使用我想要的上下文。

此解决方案avoids

  

onChange

添加一个奇怪的参数

...在我看来也传达了更多的意图:this.dispose()onNext内部调用,所以它有点像onNext.dispose(),这正是我想要的做。