npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ducks-middleware

v1.0.2

Published

function that creates a middleware function from many ducks

Downloads

17

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...