Ducks Middleware

Extract all available middleware from a ducks object and creates a middleware with all available middleware.
Alternatives To Ducks Middleware
Project NameStarsDownloadsRepos Using ThisPackages Using ThisMost Recent CommitTotal ReleasesLatest ReleaseOpen IssuesLicenseLanguage
Iron Redux298
a year ago2mitTypeScript
Painless typesafe Redux code generator
React Test Demo156
4 years agoJavaScript
Web 前端单元测试到底要怎么写?看这一篇就够了
Redux Api Call6852a year ago66August 16, 2022mitJavaScript
One declarative API to create reducers, action creators and selectors for any API calls
Redux Fetch Middleware361110 months ago39October 16, 20181mitJavaScript
The simplest middleware using fetch api for redux to send request
Fusion Baseui30
4 years ago2JavaScript
Simple app using Fusion.js and Base UI
React Redux Api Tools30113 years ago13June 23, 2020mitJavaScript
A set of tools to facilitate react-redux development and decouple logic from compontents
Redux Fetcher20417 years ago5August 12, 20162mitJavaScript
Simple isomorphic fetch for Redux
Next Mvc18
5 years ago12August 01, 2018mitJavaScript
An isomorphic mvc framework based on next.js, react, redux and immer
Hermes Js14
5 years ago18May 24, 2018mitJavaScript
Universal action dispatcher for JavaScript apps
Generator React Redux App1328 years ago6January 23, 20162JavaScript
Yeoman generator for React with Redux. Webpack, ES6, Mocha, Express.
Alternatives To Ducks Middleware
Select To Compare


Alternative Project Comparisons
Readme

ducksMiddleware building status

Extract all available middleware from a ducks object and creates a middleware with all available middleware.

It uses reducers defined as ducks, see ducks-modular-redux (aka isolated modules), and creates middleware that composes of existing middleware from ducks property middleware with no specific order that can be applyied with applyMiddleware.

Quick Use

Install with npm:

npm install ducks-middleware
// index.js
import ducksReducer from 'ducks-reducer'
import ducksMiddleware from 'ducks-middleware'

import * as comments from './comments'
import * as posts from './posts'
import * as users from './users'

const ducks = { comments, posts, users }
const reducer = ducksReducer(ducks)
const middleware = ducksMiddleware(ducks)

// ...do your stuff...

const store = createStore(
  reducer, 
  preloadedState, 
  applyMiddleware(middleware)
)

// ...do your stuff...
// comments.js
export default function commentsReducer(state, action) {
  switch (action.type) {
    // Do here your reducer magic
    default: return state
  }
}

export const middleware = store => next => action => {
  next(action);
  // Do here your middleware magic
};

// ...

ducksMiddleware(ducks)

It creates a middleware with all the middleware from the given reducers.

It assumes that ducks may have a middleware property that can be composed as a single middleware.

const ducks = { comments, posts, users }
const reducer = ducksReducer(ducks)
const middleware = ducksMiddleware(ducks)
const store = createStore(
  reducer, 
  preloadedState, 
  applyMiddleware(middleware)
)
// equivalent without ducksMiddleware
const reducer = ducksReducer({ comments, posts, users })
const store = createStore(
  reducer, 
  preloadedState, 
  applyMiddleware(comments.middleware, posts.middleware, users.middleware)
)

Because EcmaScript does not ensure any exact order to traverse Object properties, it is possible that middleware order can be altered from execution to execution.

// without ducksMiddleware any middleware order should be valid
const reducer = ducksReducer({ comments, posts, users })
const store = createStore(
  reducer, 
  preloadedState, 
  applyMiddleware(comments.middleware, users.middleware, posts.middleware)
)

If any duck has no middleware property, it is ignored.

// equivalent without ducksMiddleware in which users does not have middleware
const reducer = ducksReducer({ comments, posts, users })
const store = createStore(
  reducer, 
  preloadedState, 
  applyMiddleware(comments.middleware, posts.middleware)
)

Duck with Middleware Example

// posts.js

// Actions
export const FETCH_POSTS = 'FETCH_POSTS'
export const FETCH_POSTS_FULFILLED = 'FETCH_POSTS_FULFILLED'

// Action creators
export const fetchPosts = () => ({ type: FETCH_POSTS })
export const fetchPostsFulfilled = (payload) => ({ 
  type: FETCH_POSTS,
  payload,
})

// Selectors
export const getPosts = (state) => state.posts

// Reducer
export default function postsReducer(state = [], action) => {
  switch (action.type) {
    case FETCH_POSTS_FULFILLED:
      return action.payload
    default: return state
  }
}

// Middleware
export const middleware = ({ dispatch }) => next => async (action) => {
  next(action);
  if (action.type === FETCH_POSTS) {
    const response = await fetch('/api/posts')
    const payload = await response.json()
    dispatch(fetchPostsFulfilled(payload))
  }
}

Why middleware instead of thunks

In the previous example, the middleware was the following:

export const middleware = ({ dispatch }) => next => async (action) => {
  next(action);
  if (action.type === FETCH_POSTS) {
    const response = await fetch('/api/posts')
    const payload = await response.json()
    dispatch(fetchPostsFulfilled(payload))
  }
}

the same logic can be implemented with a thunk (in this case redux-async-thunk) which requires to replace fetchPosts with the following action creator:

export const fetchPosts = () => {
  return async ({ dispatch }) => {
    dispatch({ type: FETCH_POSTS })

    const response = await fetch('/api/posts')
    const payload = await response.json()
    dispatch(fetchPostsFulfilled(payload))
  }
}

Both codes are almost the same, and satisfies the same behavior. The difference is the extensibility and the responsibility inversion: Once you have defined fetchPosts in one duck module, you should not change it. It will always fetch posts and nothing else. ¿What if you decide that you want also to fetch comments at the same time than posts? You cannot unless you modify fetchPosts.

With middlewares this limitation dissapears. Now in your comments duck you can add a middleware to add comments, just implement another middleware like the previous one but fetching comments:

import FETCH_POSTS from '../posts';

// ...

export const middleware = ({ dispatch }) => next => async (action) => {
  next(action);
  if (action.type === FETCH_POSTS) {
    dispatch(fetchComments())
  } else if (action.type === FETCH_COMMENTS) {
    const response = await fetch('/api/comments')
    const payload = await response.json()
    dispatch(fetchCommentsFulfilled(payload))
  }
}

Or, if you consider more convenient, keep comments duck simple and add fetch comments when fetching posts in an additional duck.

// ./comments.js

// ...

export const middleware = ({ dispatch }) => next => async (action) => {
  next(action);
  if (action.type === FETCH_COMMENTS) {
    const response = await fetch('/api/comments')
    const payload = await response.json()
    dispatch(fetchCommentsFulfilled(payload))
  }
}
// ./posts-comments.js
import FETCH_POSTS from '../posts';
import fetchComments from '../comments';

export const middleware = ({ dispatch }) => next => action => {
  next(action);
  if (action.type === FETCH_POSTS) {
    dispatch(fetchComments())
  }
}

Middlewares and system events

You can use middleware to generate actions from system events, for example:

// ./window-scroll.js
export const SCROLL_CHANGED = 'SCROLL_CHANGED';
export const scrollChanged = () => ({ type: SCROLL_CHANGED });

export const middleware = ({ dispatch }) => {
  if (typeof window !== 'undefined') {
    window.addEventListener('scroll', () => {
      dispatch(scrollChanged())
    })
  }

  return next => next
}

See also

ducks-reducer to compose ducks reducers.

import ducksReducer from 'ducks-reducer'
import ducksMiddleware from 'ducks-middleware'

import * as comments from './comments'
import * as posts from './posts'
import * as users from './users'

const ducks = { comments, posts, users }
const reducer = ducksReducer(ducks)
const middleware = ducksMiddleware(ducks)

// ...do your stuff...

const store = createStore(
  reducer, 
  preloadedState, 
  applyMiddleware(middleware)
)

// ...do your stuff...
Popular Fetch Projects
Popular Reducer Projects
Popular Networking Categories

Get A Weekly Email With Trending Projects For These Categories
No Spam. Unsubscribe easily at any time.
Javascript
Fetch
Reducer