5.0.4 • Published 7 years ago

melcore v5.0.4

Weekly downloads
3
License
Unlicense
Repository
github
Last release
7 years ago

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())
})
5.0.4

7 years ago

5.0.3

7 years ago

5.0.2

7 years ago

5.0.1

7 years ago

4.0.2

7 years ago

4.0.1

7 years ago

4.0.0

7 years ago

3.0.0

7 years ago

2.0.0

7 years ago

1.0.0

8 years ago