redux-direct

Use Redux without string constants

Usage no npm install needed!

<script type="module">
  import reduxDirect from 'https://cdn.skypack.dev/redux-direct';
</script>

README

Build Status

redux-direct

Use Redux without string constants.

With redux-direct you can use your action creators directly inside the reducers instead of having to define string constants for each action.

Usage

Each action creator created by redux-direct has a toString() method that returns the action type. Combined with ES6 computed properties this allows you to write the following code:

import { createAction, createReducer } from 'redux-direct';

const increment = createAction('INCREMENT');
const decrement = createAction('DECREMENT');

const reducer = createReducer(initialState, {
  [increment]: (state, action) => ({ ...state, counter: state.counter++ }),
  [decrement]: (state, action) => ({ ...state, counter: state.counter-- }),
});

Action Payload

By default, the first argument passed to an action creator is used as payload, the second as meta. Calling increment(2, { foo: 'bar' }) will create the following action:

{
  type: 'INCREMENT',
  payload: 2,
  meta: {
    foo: 'bar',
  },
}

You can dynamically create payloads by providing a payload creator function:

createAction('INCREMENT' by => by || 1);

An additional third argument can be specified to provide predefined meta data. To dynamically create the data you can pass a function that will be called with the same arguments as the payload creator.

Multiple Actions

Usually you want to create multiple actions at once:

import { createActions } from 'redux-direct';
export default createActions('counter', {
  increment: by => by || 1, // payload creator
  decrement: {              // payload and meta
    payload: by => by || 1,
    meta: 'foo',
  },
  reset: false, // use action arguments as payload and meta
});

This will create actions with the types COUNTER_INCREMENT, COUNTER_DECREMENT and COUNTER_RESET.

You can also use a path like __filename as prefix, in which case the file's basename (without the extension) is used.

Action Phases

If you use a middleware like redux-promised, redux-async or redux-promise-middleware that dispatches multiple actions with different suffixes, you can specify a handler for each suffix in your reducer:

createReducer(initialState, {
  [loadUser]: {
    _PENDING: (state, action) => ({ ...state, user: null, error: null }),
    _RESOLVED: (state, action) => ({ ...state, user: action.payload }),
    _REJECTED: (state, action) => ({ ...state, user: null, error: action.payload }),
  }
});

Some middleware implementations like redux-simple-promise dispatch an action without any suffix for the pending state. Since redux-direct uses the same method to build the final action types as it does in createActions() the following definition can be used:

const loadUser = createAction('LOAD_USER');
createReducer(initialState, {
  [loadUser]: {
    _: (state, action) => (/* LOAD_USER */),
    resolved: (state, action) => (/* LOAD_USER_RESOLVED */),
    rejected: (state, action) => (/* LOAD_USER_REJECTED */),
  }
});

API

createAction

createAction(type, [payloadCreator], [metaCreator])

Returns a function that creates FSA compliant actions of the given type. See usage for a basic example.

createActions

createActions([prefix], definition)

Returns an object with methods to create the described actions. See Multiple Actions for an example.

createReducer

createReducer(initialState, handlers)

Returns a reducer function. See usage for a basic example or Action Phases for a more advanced use case.

License

MIT