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

melcore

v5.0.4

Published

Simple 'Redux-ish' functions for state management

Downloads

13

Readme

Melcore

Minimal Redux-ish implementation (just 100 lines of code).

Use it with React, Inferno, Choo, Mithril, whatever really.

I really just wanted something like Redux with a nicer way of creating reducers and setting up the store.

The api is also whittled down further. There's no dedicated way to add middleware, since you can just add additional "reducers" before and after. Pre-populating state is also done just by dispatching an "initialize" type action and having reducers form initial state from there. Also rather than the store having one reducer that is put together via "combineReducers", it is the default to just have the store take an array of reducers.

The Store

A store is an object that contains a single atom of the application's state, and registered reducers that divide the responsibilities of creating a new state with every action.

const createStore = require('melcore').createStore

const store = createStore([
	require('./reducers/todos'),
	require('./reducers/counts')
])

module.exports = store

(In the above example, the store is being passed an array of reducers. Alternatively, )

The store has several functions: dispatch, getState, getPrev, and createReducer

Get State

To retrieve the store's current state atom, call store.getState().

You can retrieve the store's state prior to the last dispatch call with store.getPrev().

Reducers

Reducers are done in such a way that they will not fail silently if you try to respond to an undefined action type. This is better than a switch statement since you may accidentally misspell a constant and case won't care

const __INIT__ = require('melcore').__INIT__
const createReducer = require('melcore').createReducer
const constants = require('./constants')

const todos = createReducer('todos')
	.on(__INIT__, function () {
		return []
	})
	.on(constants.CREATE_TODO, function (action, oldState) {
		return oldState.concat([action.todo])
	})
	.on(constants.REMOVE_TODO, function (action, oldState) {
		return oldState.filter(function (todo) {
			return todo.id !== action.targetId
		})
	})

module.exports = todos

createReducer always takes a string as its only argument. It specifies which piece of oldState it will receive from the store on every action, and which state it is expected to return on every handler.

Instead of creating a reducer via melcore.createReducer and adding it to the store's array of reducers, you can also call createReducer directly on the store:

const store = require('./store')

store.createReducer('message')
  .on('__INIT__', function () {
    return 'Hello World!'
  })
  .on('message/editMessage', function (oldState, message) {
    return message
  })

The reducer will now be part of the store's main reducer. This is nice when you have a modular file structure and don't want to go back to edit your main store.js file every time you create a new module in your app.

Dispatch

To dispatch an action to the store, simply call it's dispatch method.

store.dispatch('ACTION_NAME', {data: 'stuff'})

The second argument to dispatch (optional) is which whatever payload you wish to send as part of that action.

Initialization

The store gets it's initial state idiomatically by just dispatching an action agreed upon to be the "start" for your application. I prefer this as to increasing the function signature to setup reducers and the store as Redux does.

Calling store.init() will dispatch the __INIT__ action to all reducers with no initial arguments. Do this on app start, and have each reducer return their initial state as a result of this action.

Of course, you can always just define and use your own action string as the "init", rather than the built-in one. store.init() is really just there to make this convention explicit.

Handling thunks

Won't you take me to... thunk-y toooown?

Action creators that return functions receive the store's dispatch method as the callback argument. This is useful for when an action is asynchronous.

const store = require('./store')

function getStuff (dispatch) {
  m.request({ .. }).then(function (res) {
    dispatch: {
      type: 'GOT_STUFF',
      stuff: res
    }
  })
}

store.dispatch( getStuff )

Mutating State

It is best practice to not mutate state inside a reducer. The state returned should be

  1. A new object containing no references that would be linked to previous state
  2. The previous state, untouched.

I highly recommend Icepick as a way to deal with this. Immutable.js is very good as well.

Plugins? Middleware?

Just given how Melcore works, all you need is the ability to wrap the dispatch method.

For convenience, wrapDispatch is provided for you on the store. Though you can just wrap the method as you would normally.

store.wrapDispatch(function (dispatch, action, payload) {
  console.log('this action is about to be dispatched: ', action)
  
  dispatch(action, payload)
  
  console.log('the new application state is: ', store.getState())
})