crudReducer

Peter Gundel edited this page Jul 21, 2017 · 5 revisions

The crudReducer includes a set of sane defaults to help you build CRUD applications. It is meant to be used alongside actions generated by actionsIncludingCrud and crudState, both exported by redux-belt. You can use it to implement the full functionality of your reducer:

const actions = actionsIncludingCrud('books')
const booksReducer = (state = crudState, action = {}) => crudReducer(state, action, actions)

...but you'll most likely use it to implement a part of the functionality of your reducer:

function booksReducer(state = { ...crudState, ...someDefaultState }, action = {}) {
  switch (action.type) {
    case actions.YOUR_OWN_ACTION_TYPE: {
      // Do stuff...
    }
    case actions.YOUR_OTHER_ACTION_TYPE: {
      // Do other stuff...
    }
    default :
      return crudReducer(state, action, actions)
  }
}

Sane defaults for your application's state.

The functionality the crudReducer implements is basic. But it is used in so many places in any CRUD-based application, that it's worth abstracting and standardizing into a set of default behavior. Before learning what crudReducer does for you, let's first take a look at the default crudState exported by redux-belt:

export const crudState = {
  changes: {},
  errors: [],
  filters: {},
  loading: {
    create: false,
    delete: false,
    index: false,
    single: false,
    update: false,
  },
  index: [],
  meta: {},
  single: {},
}

We've found these keys will satisfy almost all needs a basic CRUD application has. Here's what each one of these is intended for:

  • index: holds an array of items (let's call these items Books), which represents a list of all the data we would want to show in an index.

  • meta: holds the meta-data related to the index view. Usually this is pagination information. By default, crudReducer will not store anything here. If you'd like to use the meta key, you will need a custom extractor using configureCrudReducer. More on that here.

  • filters: holds the parameters for your index view calls.

  • single: holds a single object - in this case, a Book.

  • changes: holds a single object, with the same shape as a Book. changes is meant to hold the results of your application's updates on the current single Book. For example, if you have a view in our app to update the description of a book, you can use actions.setChanges({ description: 'Awesome sci-fi' }) (setChanges is generated by actionsIncludingCrud) and the new description will be saved in the changes of your books reducer. The changes are meant to be compared with the single Book and used later on by calling update, which should trigger the network request for your update.

  • loading: holds key/value pairs with booleans indicating the status of each type of CRUD action and the different views.

  • errors: is meant to hold an array of the errors reported by your back-end after the last failed network request.

What does it do for me?

Understanding the default functionality is trivial with crudState in mind, and can be achieved by simply reading its tests. Here's a summary assuming we've created our actions like this:

const booksActions = actionsIncludingCrud('books')
  • Dispatching booksActions.create({ /* Some info about the book */ }) will set loading.create to true. Implement the network request and use simpleAsync to call your API. simpleAsync will dispatch an action with type CREATE_SUCCESS or CREATE_FAILURE and your one-line reducer will clear the loading state for create and populate single with the new book, or errors with the errors.
  • Dispatching booksActions.fetchIndex({ /* Some params */}) will set loading.index to true and (assuming you've implemented a network request with simpleAsync) populate index and (if configured) meta, and clear the loading state for index. If you passed an object with params, the filters state will be populated too.
  • Dispatching booksActions.fetchSingle({ id: '123' }) will do the analog of what fetchIndex does, except fetchSingle has nothing to do with the filters. Uses the single key.
  • Dispatching booksActions.delete({ id: '123' }) will just manage the loading.delete key. This decision is based on the fact that most DELETE requests on REST APIs are status-based and don't usually report a body with errors but only a status.
  • Dispatching booksActions.update({ /* Some info about a book and hopefully an ID to identify it }) will trigger effects similar to create. In addition, if the network request succeeds, UPDATE_SUCCESS will set changes to an empty object (because we're done editing the book) and will replace the corresponding entry in index if it exists, trying to find it by id.
  • Dispatching bookingsActions.setChanges({ /* Some info, maybe a new book? */}) will ignore the existing changes and set them to the passed value.
  • Dispatching bookingsActions.mergeChanges({ /* Some new info about a book */}) will merge the passed information with the existing changes.

By default, crudReducer will pass the entire payload into its respective key and it will pass an empty object in the case of meta. In most applications, you'll want to configure your reducer to take the interesting part of the payload. To do this, read the documentation for configureCrudReducer.

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.