简体   繁体   中英

Redux: turn middleware on and off

I am looking for a way to turn a middleware on and off. I introduced a tutorial functionality - I listen to what the user is doing with the UI by checking each action with a "guidance" middleware. if the user clicks on the right place he moves to the next step in the tutorial. However this behaviour is only needed when the tutorial mode is on. Any ideas?

const store = createStore(holoApp, compose(applyMiddleware(timestamp, ReduxThunk, autosave, guidance),
window.devToolsExtension ? window.devToolsExtension() : f => f)); 

for now my solution was to keep the "on" switch in a guidanceState reducer and dirty check it in the middleware:

const guidance = store => next => action => {

    let result = next(action)

    const state = store.getState();
    const { guidanceState } = state;
    const { on } = guidanceState;

    if (on) {

 ....

However, ~95% of the time the tutorial mode would be off so dirty checking every action all the time feels a bit, well, dirty... ;) Any other ways?

Don't do stateful things in middleware (unless you have a good pattern for managing that state, like Sagas). Don't do stateful things with your middleware stack at all if you can avoid it. (If you must do so, @TimoSta's solution is the correct one).

Instead, manage your tours with a reducer:

const finalReducer = combineReducers({
  // Your other reducers
  tourState: tourReducer
});

function tourReducer(state = initalTourState, action) {
  switch(action.type) {
    case TOUR_LAST_STEP:
      return /* compose next tour step state here */;
    case TOUR_NEXT_STEP:
      return /* compose last tour step state here */;
    case TOUR_CLOSE:
      return undefined; // Nothing to do in this case
    default:
      return state;
  }
}

Then, in your application use the current state of tourState to move the highlighting, and if there is nothing in tourState , turn the tour off.

store.subscribe(() => {
  const state = store.getState();
  if (state.tourState) {
    tourManager.setTourStep(state.tourState);
  } else {
    tourManager.close();
  }
});

You don't have to use a stateful tour manager either - if you're using React it could just be a component that pulls out tourState with a connect wrapper and renders null if there is no state:

// waves hands vigorously
const TourComponent = (props) => {
  if (props.currentStep) return <TourStep ...props.currentStep />;
  return null;
}

I don't know of any way to replace middlewares on the fly via redux's API.

Instead, you could create a completely new store with the old store's state as initial state and the new set of middlewares. This may work seamlessly with your application.

Three ideas you could consider:

  • Have the middleware listen for "GUIDANCE_START" and "GUIDANCE_STOP" actions. When those come through, update some behavior, and don't actually pass them to next .
  • You could write a middleware that constructs its own middleware pipeline internally, and dynamically adds and removes the guidance middleware as needed (somewhat related discussion at replaceMiddleware feature for use with lazy-loaded modules )
  • This might be a good use case for something like a saga, rather than a middleware. I know I've seen discussions of using sagas for onboarding workflows, such as the Key&Pad app (source: key-and-pad )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM