Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Middleware guide #342

Open
dolanmiu opened this issue May 2, 2018 · 6 comments
Open

Middleware guide #342

dolanmiu opened this issue May 2, 2018 · 6 comments

Comments

@dolanmiu
Copy link

dolanmiu commented May 2, 2018

I am trying to learn how to do middleware, but I cannot see any good resources to learn from.

I checked:

  1. http://reswift.github.io/ReSwift
  2. http://reswift.github.io/ReSwift/master/Other%20Typealiases.html#/s:7ReSwift10Middlewarea
  3. Multiple example projects
  4. And of course the README

Can someone help?

@DivineDominion
Copy link
Contributor

DivineDominion commented May 3, 2018

Maybe you could be the one missing contributor to a Middleware guide? :) I'd be happy to help.

Creation

To get started, creating a simple Middleware with the nested closures looks like this:

import Foundation
import ReSwift

let loggingMiddleware: Middleware<AppState> = { dispatch, getState in
    return { next in
        return { action in
            print(">  \(action)")
            next(action)
        }
    }
}

Basics

  1. You need to call next(action) to pass the action on to the next object in the chain -- that'd be another Middleware, or in case of the last Middleware, it'd be the main reducer. In other words, if you do not call next(action), you can swallow or filter actions in Middlewares.

  2. When you call dispatch from within a Middleware, the whole ReSwift store dispatching will be executed and finished before the next line in the Middleware is reached since ReSwift works synchronously. That means you can react to incoming actions (think: triggers) in Middleware by dispatching a whole chain of new actions; and you can do so before the trigger itself is reduced to a state update, or after.

    let bananaMiddleware: Middleware<AppState> = { dispatch, getState in
        return { next in
            return { action in
    
                guard let bananaAction = action as? BananaAction else {
                    // Do not react to any other action in any special way:
                    next(action)
                    return
                }
    
                dispatch(bananaAction.someSideEffectOrServerRequest) // perhaps an ActionCreator
                dispatch(ActionBeforeBananaActionIsReduced())
                next(action)
                dispatch(ActionAfterBananaActionIsReduced())
            }
        }
    }

@Tavernari
Copy link

Tavernari commented Dec 1, 2018

For this topic, I made simple tools and I called it ReSwiftMiddleware.

A simple example of how to use this

import Foundation
import ReSwift

struct MiddlewareLogger : MiddlewareExecutor{
    
    let logger:LogConsole;
    
    init(logger:LogConsole) {
        self.logger = logger;
    }
    
    func execute(action: Action, state: StateType?) -> Action? {
        
        if let counterAction = action as? CounterActions{
            self.logger.register(value: counterAction.toString())
        }
    
        return action
    }
}

If you need to interrupt the flow, you return nil on execute method.

The example of implementing MiddlewaresCollection.

let middlewaresCollection = MiddlewaresCollection<CounterState>();
let middlewareLogger = MiddlewareLogger(logger: logger);

middlewaresCollection.add(middlewareLogger);

Now an example of how to connect this collection to [ReSwfit]

let store = Store<CounterState>(reducer: reducerCounterState, state: CounterState(), middleware: middlewaresCollection.middlewares)

https://github.com/Tavernari/ReSwiftMiddleware

@dani-mp
Copy link
Contributor

dani-mp commented Dec 2, 2018

Hi, @Tavernari.

Note that this would have some limitations compared to “normal” ReSwift middleware functions, maybe you want to add them in the README:

  • It’s not possible to get the current state after an effect has been run. You only have access to the previous state passed to the execute function, that might be obsolete when you use it.
  • It’s not possible to dispatch new actions, only pass the current one through or skip it.
  • It’s not possible to apply a side effect after the action has continued with the middleware chain (potentially having reached the reducers).

Sent with GitHawk

@Tavernari
Copy link

Tavernari commented Dec 3, 2018

Hi, @Tavernari.

Note that this would have some limitations compared to “normal” ReSwift middleware functions, maybe you want to add them in the README:

  • It’s not possible to get the current state after an effect has been run. You only have access to the previous state passed to the execute function, that might be obsolete when you use it.
  • It’s not possible to dispatch new actions, only pass the current one through or skip it.
  • It’s not possible to apply a side effect after the action has continued with the middleware chain (potentially having reached the reducers).

Sent with GitHawk

Hi @danielmartinprieto,

Thanks for the observations, I made some updates on version 0.1.2 and now I think it's not more obsolete and you can dispatch action direct from inside the MiddlewareExecutor.

struct MiddlewareLogger : MiddlewareExecutor{
    
    let logger:LogConsole;
    
    init(logger:LogConsole) {
        self.logger = logger;
    }
    
    func execute(action: Action, getState: @escaping () -> StateType?, dispatch: @escaping DispatchFunction) -> Action? {
        
        if let counterAction = action as? CounterActions{
            self.logger.register(value: counterAction.toString())
        }

        return action
    }
} 

https://github.com/Tavernari/ReSwiftMiddleware

@atacengiz
Copy link

@Tavernari I was extremely confused with ReSwiftMiddleware because it's just a function which makes it not easy to use dependency injection for testing purposes. I am using your MiddlewareExecutor and it helped me greatly to both understand the general concept and with my testing.

@Tavernari
Copy link

@atacengiz My idea on this framework is to help developers to use Middlewares, if you look the syntax with many functions inside functions, for me is not clear and demand more knowledge about swift syntax, so ReSwiftMiddleware helps developers to implement Middlewares easily using objects.

I hope to help many others to use ReSwift because I really love this framework.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants