You signed in with another tab or window.Reload to refresh your session.You signed out in another tab or window.Reload to refresh your session.You switched accounts on another tab or window.Reload to refresh your session.Dismiss alert
Gated middleware is a middleware that holds an inner middleware that could be either active or not.
There are two gate variations that can be used: by action or by state.
GatedMiddleware by action:
It holds an internal state, calledgate state, that determines whether or not the inner middleware should be inactive orbypass mode.This can be changed dynamically.
It starts with an initial gate state, called "default gate state". From that point on, it will evaluate all incoming actions to detect a "controlaction", which is an action for switching on or off the gate state. This control action is detected thanks to a control action map closure or acontrol action map KeyPath configured in the GatedMiddleware's init, which from a given input action allows the user to inform either or not thisis a control action, by returning an Optional instance of that ControlAction (or nil in case it's a regular action).
The init also requires some comparison values, for turnOn or turnOff the gate. If it's a control action, and it's equals to turn on, it will setthe inner middleware to active. If it's a control action, and it's equals to turn off, it will set the inner middleware to bypass. If it's not acontrol action, or it's not equals to any of the comparison values, the gate will remain untouched.
The gated middleware by action will ALWAYS forward control actions to inner middlewares, regardless of their gate state (active or bypass) andregardless of the turn on/turn off comparison result. This will allow important actions like disabling or enabling the inner middleware forcontrol actions, so for example, even for when we close the gate we still want to tell the inner middleware that it's gonna be bypassed and itshould kill all of its timers or async side-effects.
GatedMiddleware by state:
It won't hold any internal state, instead, it will use some state from your App Global State. You're responsible for mutating this state from yourown reducers. At any point that the state tells that this middleware isactive, it's gonna handle actions and be able to dispatch new actions.However, whenever the state is set tobypass, this middleware will ignore incoming actions and won't be able to dispatch any new action.
When handling actions, the state is evaluated before reducers, so whatever state is set BEFORE reducer, will define if the inner middleware willbe called before and after the reducer, even if the reducer changes that value. An action that changes the state fromactive tobypass, willtrigger inner middleware before and after the reducer, and after the reducer that value will be already set tobypass, so you can stop timersand async tasks. An action that changes the state frombypass toactive, will not trigger the inner middleware before the reducer nor afterit, so you may want to send a second action to start the middleware timers again, because the gated middleware can't do that for you.
Examples:
Gated by action, simple example
// sourcery: Prismenum AppAction { case something case anotherSomething case dynamicMiddlewares(DynamicMiddlewareAction)}// sourcery: Prismenum DynamicMiddlewareAction: Equatable { case toggleCrashReportsMiddleware(enable: Bool)}let gatedCrashReportsMiddleware = CrashReportsMiddleware .init() .gated( controlAction: \AppAction.dynamicMiddlewares?.toggleCrashReportsMiddleware?.enable, default: .active )
Gated by action, with custom comparison:
// sourcery: Prismenum AppAction { case something case anotherSomething case dynamicMiddlewares(DynamicMiddlewareAction)}// sourcery: Prismenum DynamicMiddlewareAction: Equatable { case controlCrashReportsMiddleware(controlAction: MiddlewareControlAction)}// sourcery: Prismenum MiddlewareControlAction: Equatable { case activate case bypass case sayHello}let gatedCrashReportsMiddleware = CrashReportsMiddleware .init() .gated( controlAction: \AppAction.dynamicMiddlewares?.controlCrashReportsMiddleware, turnOn: MiddlewareControlAction.activate, turnOff: MiddlewareControlAction.bypass, default: .active ) // in this example, MiddlewareControlAction.activate will activate the crash reports, // MiddlewareControlAction.bypass will disable the crash reports, and // MiddlewareControlAction.sayHello won't change the gate state, but will be forwarded to the // crash reports middleware regardless of its current state.
You can also lift the inner middleware before gating it, in case the AppActions or AppStates don't match. Evidently lift can also be doneafter the gated middleware if this is what you need.
Gating composed middlewares will disable or enable all of them at once, and the control action will be the same and be forwarded to allthe inner middlewares all the times. If this is not what you need, you need disabling them individually, you can first gate them and withthe gated collection you compose them.
This has no interference on Reducers and this doesn't change the AppState in any way. GatedMiddleware only matches AppState with its innermiddleware to allow proxying thegetState context.
About the examples and code generation
All examples above use Sourcery Prism templates to simplify traversing action trees, but theGatedMiddleware offers closures in case youprefer switch/case approach or other custom functions.