From 93b6c5447904ce7e383477ae34a11dc9c8f1482b Mon Sep 17 00:00:00 2001 From: Lucien Greathouse Date: Fri, 18 May 2018 12:27:18 -0700 Subject: [PATCH] Flip middleware execution order --- CHANGELOG.md | 1 + docs/api-reference.md | 10 +++++++++- lib/Store.lua | 3 ++- lib/Store.spec.lua | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fb3509..bdbdae1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * Thunks are no longer enabled by default, use `Rodux.thunkMiddleware` to add them back. * Added `Rodux.loggerMiddleware` as a simple debugger * The middleware API changed in [#29](https://github.com/Roblox/rodux/pull/29) in a backwards-incompatible way! + * Middleware now run left-to-right instead of right-to-left! * Errors thrown in `changed` event now have correct stack traces ([#27](https://github.com/Roblox/rodux/pull/27)) ## Public Release (December 13, 2017) diff --git a/docs/api-reference.md b/docs/api-reference.md index fb1f508..b56d033 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -153,7 +153,7 @@ local reducer = createReducer(initialState, { ``` ## Middleware -Rodux provides an API that allows changing the way that actions are dispatched called *middleware*. To attach middlewares to a store, pass a list of middleware as the third argument to `Store.new`. +Rodux provides an API that allows changing the way that actions are dispatched called *middleware*. To attach middleware to a store, pass a list of middleware as the third argument to `Store.new`. !!! warn The middleware API changed in [#29](https://github.com/Roblox/rodux/pull/29) -- middleware written against the old API will not work! @@ -180,6 +180,14 @@ end Rodux also ships with several middleware that address common use-cases. +To apply middleware, pass a list of middleware as the third argument to `Store.new`: + +```lua +local store = Store.new(reducer, initialState, { simpleLogger }) +``` + +Middleware runs from left to right when an action is dispatched. That means that if a middleware does not call `nextDispatch` when handling an action, any middleware after it will not run. + ### Rodux.loggerMiddleware A middleware that logs actions and the new state that results from them. diff --git a/lib/Store.lua b/lib/Store.lua index c688540..90aa02f 100644 --- a/lib/Store.lua +++ b/lib/Store.lua @@ -53,7 +53,8 @@ function Store.new(reducer, initialState, middlewares) return unboundDispatch(self, ...) end - for _, middleware in ipairs(middlewares) do + for i = #middlewares, 1, -1 do + local middleware = middlewares[i] dispatch = middleware(dispatch, self) end diff --git a/lib/Store.spec.lua b/lib/Store.spec.lua index c7aa014..5e7a5fd 100644 --- a/lib/Store.spec.lua +++ b/lib/Store.spec.lua @@ -85,6 +85,44 @@ return function() store:destruct() end) + it("should execute middleware left-to-right", function() + local events = {} + + local function reducer(state) + return state + end + + local function middlewareA(nextDispatch, store) + table.insert(events, "instantiate a") + return function(action) + table.insert(events, "execute a") + return nextDispatch(action) + end + end + + local function middlewareB(nextDispatch, store) + table.insert(events, "instantiate b") + return function(action) + table.insert(events, "execute b") + return nextDispatch(action) + end + end + + local store = Store.new(reducer, 5, { middlewareA, middlewareB }) + + expect(#events).to.equal(2) + expect(events[1]).to.equal("instantiate b") + expect(events[2]).to.equal("instantiate a") + + store:dispatch({ + type = "test", + }) + + expect(#events).to.equal(4) + expect(events[3]).to.equal("execute a") + expect(events[4]).to.equal("execute b") + end) + it("should send an initial action with a 'type' field", function() local lastAction local callCount = 0