Reswift: Provide Store Enhancing Mechanism

Created on 15 Dec 2015  路  14Comments  路  Source: ReSwift/ReSwift

We should provide a store enhancing mechanism, just as Redux, that allows us to extend the stores capabilities without subclassing. Eventually the Swift-Flow-Recorder should become a store enhancer instead of a store subclass.

Most helpful comment

@timojaask That is incorrect. Middleware occurs _around_ reducers.

public func persistanceMiddleware() -> Middleware<PersistenceMiddlewareState> {
    return { dispatch, getState in
        return { next in
            return { action in

                // Any code here is before the reducers

                next(action) // This executes the reducers

                // Any code here is after the reducers

            }
        }
    }
}

All 14 comments

Yes, may I suggest that we stick with the same middleware pattern from Redux. Then it should be pretty easy to port over a robust optimistic update solution such as redux-optimist.

BTW, great work on this library. We've just decided to use this on our next project and I'm looking forward to contributing.

@aexmachina thanks a lot for your response! The goal is to have a middleware API that resembles the one of redux. The folks over at https://github.com/reduxkit/reduxkit are actually close to feature parity with the original Redux API, and we are currently considering merging efforts.

If we end up with separate projects though, I definitely want to bring a redux-like middleware implementation to Swift Flow soon!

FWIW, your implementation is much nicer than ReduxKit :smile: I think Swift Flow manages to "go with the grain" of Swift's type system, rather than getting caught up in generics.

Will tackle store enhancing and middleware separately. Middleware is being implemented here: #9

How were you envisioning the usage of a store enhancer?

let newStore = SomeStoreEnhancer(store: existingStore)

store.enhance(enhancer: SomeStoreEnhancer)

let store = SomeStoreEnhancer(MainStore)(reducer: reducer, appState: TestStringAppState())

The redux store enhancers take a createStore function, enhance it, and return a new wrapped createStore function. The createStore equivalent here would most likely be MainStore#init. For the store enhancer to have direct access to the dispatch and state, it might be best for store enhancer to just be a store function.

I guess one test would be to implement applyMiddleware as a store enhancer.

@agentk I originally opened this ticket for the implementation of middleware, which has been closed in #9. Middleware was very high priority - I'm not entirely sure about store enhancers yet.

This is actually one of the areas where I'm wondering if there's an implementation more natural to Swift. From the original documentation of redux:

A store enhancer is a higher-order function that composes a store creator to return a new, enhanced store creator. This is similar to middleware in that it allows you to alter the store interface in a composable way.

It seems like in Swift this would practically mean a one-to-one mapping between a specific store enhancer and a specific store type, since unlike in JS you cannot modify the interface of the store without introducing a new type. If my assumption is correct, it also means that store enhancers couldn't be composed.

If both assumptions are correct I think it would be better to have individual types for the different stores one can create.

But you can see I still have a little bit of research to do :smile:

So far it doesn't seem we have a need for actual store enhancers, therefore I'm closing this issue for now.

Yeah, I think middleware is the crucial requirement here.

@Ben-G I am trying to implement state persister, for which I feel that store enhancer would be the best option.

Middleware cannot be used because it's called before store changes.

Another option is to make persister a StoreSubscriber and subscribe to store changes, but you can't use StoreSubscriber as a function parameter because it's a protocol with associated types, which makes it very clumsy and I can't figure out how to write tests for that.

So it seems like there's a need for store enhancer. I know you're not working on this project anymore, but since you have already looked at the enhancer, I wanted to ask you if you could share any findings. Were there any issues or gotchas with implementing an enhancer?

EDIT: I guess there really is no way to avoid using StoreSubscriber no matter what approach I use. So I guess I'll just try to work with it and see what I can come up with.

@timojaask Middleware can act after the store changes. Here is an example:

public func persistanceMiddleware() -> Middleware<PersistenceMiddlewareState> {
    return { dispatch, getState in
        return { next in
            return { action in
                next(action) // This executes the next middleware, and the reducers at the end of the middleware chain.

                // Any call to `getState` here or below is after the reducers have worked.
                getState()?.convertToData().writeToFile(myFile)
            }
        }
    }
}

I didn't tried it to use getState() after next(action), I have to think about it.

@timojaask That is incorrect. Middleware occurs _around_ reducers.

public func persistanceMiddleware() -> Middleware<PersistenceMiddlewareState> {
    return { dispatch, getState in
        return { next in
            return { action in

                // Any code here is before the reducers

                next(action) // This executes the reducers

                // Any code here is after the reducers

            }
        }
    }
}

Sorry, I realized my comment was incorrect, so I removed it. I guess you posted your response around the same time. Thanks for providing the solution! I'll test it out.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MichaelGuoXY picture MichaelGuoXY  路  7Comments

krema picture krema  路  6Comments

steve21124 picture steve21124  路  11Comments

dolanmiu picture dolanmiu  路  7Comments

adamhongmy picture adamhongmy  路  3Comments