Skip to content

Simple creation of actions and reducers in Redux

License

Notifications You must be signed in to change notification settings

michael-sulyak/redux-class-decorators

Repository files navigation

redux-class-decorators

NPM Scrutinizer Code Quality Build Status GitHub Issues Gitter License

Table Of Contents

Installation

npm

npm install redux-class-decorators

yarn

yarn add redux-class-decorators

Overview

Writing reducers can be annoying, it takes time to create actionTypes, and actions, and to put it all into a switch. The benefits of this package is that you don't have to manage a separate actionTypes file. You get to define actions and a reducer in classes and all your types and API calls will live on just some objects. Just a matter of preference.

You haven't to declare or manage string constants. This package meets the standard for stream action objects and will automatically declare string constants for types. Also this package works well with redux-thunk.

If you have any questions, you can see examples of use.

back to top


How To Use

Redux recommends creating constants, action creators and reducers separately. And we try to stick to this rule.

Reducer:

import { ReducerClass } from 'redux-class-decorators'

@ReducerClass('Profile')
class ProfileReducer {
  static initialState = {
    value: null,
    loading: false,
  }

  static startLoading(state, action) {
    return {
      ...state,
      loading: true,
    }
  }

  static finishLoading(state, action) {
    return {
      ...state,
      value: action.payload,
      loading: false,
    }
  }

  static clear(state) {
    return {
      ...state,
      value: null,
      loading: false,
    }
  }
}

Actions:

import { ActionClass } from 'redux-class-decorators'

@ActionClass
class ProfileActionSet {
  static get() {
    return (dispatch, getState) => {
      dispatch({
        type: ProfileReducer.startLoading,
      })

      const profile = { id: 1, name: 'Mike' }

      dispatch({
        type: ProfileReducer.finishLoading,
        payload: profile,
      })
    }
  }

  static clear() {
    return {
      type: ProfileActionSet.clear,
    }
  }
}

Usage:

// Create store
const store = createStore(ProfileReducer.$reducer, null, applyMiddleware(thunk))

// Get dispatch
const dispatch = store.dispatch

dispatch(ProfileActionSet.get())
// Actions:
// { type: 'PROFILE__START_LOADING' }
// { type: 'PROFILE__FINISH_LOADING', payload: { id: 1, name: 'Mike' } }
// state == { value: { id: 1, name: 'Mike' }, loading: false }

dispatch(Something.clear()) // { type: 'PROFILE__CLEAR' }
// state == { value: null, loading: false }

Example of using redux-class-decorators:

back to top


Bonus

PlumbingActionClass allows you to use one class to work with different instances.

Reducer:

import { PlumbingReducerClass } from 'redux-class-decorators'

@PlumbingReducerClass('Banner')
class BannerReducer {
  static $getInitialState() {
    return {
      value: null,
    }
  }

  static setValue(state, action) {
    return {
      ...state,
      value: action.payload,
    }
  }
}

Actions:

import { PlumbingActionClass } from 'redux-class-decorators'

@PlumbingActionClass
class Banner {
  static $getIndex(params) {
    return params.type
  }

  static add(newValue) {
     return {
       type: BannerReducer.setValue,
       payload: newValue,
     }
  }
}

Usage:

// Create store
const store = createStore(BannerReducer.$reducer, null, applyMiddleware(thunk))

// Get dispatch
const dispatch = store.dispatch

dispatch(Banner.add({ type: 'left', text: 'Test1' }))
// { type: 'BANNER__SET_VALUE', payload: 5 }

dispatch(Banner.add({ type: 'right', text: 'Test2' }))
// { type: 'BANNER__SET_VALUE', payload: 10 }

// state == { 'left': { value: 'Test1' }, 'right': { value: 'Test2' } }

back to top


License

MIT