Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ejoy-define

Package Overview
Dependencies
Maintainers
1
Versions
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ejoy-define

Define action constants for Redux

  • 1.1.1
  • latest
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
1
Maintainers
1
Weekly downloads
 
Created
Source

redux-define

Join the chat at https://gitter.im/smeijer/redux-define

build status

NPM

Installation

with npm:

npm install --save redux-define

or yarn:

yarn add redux-define

If you don’t use npm, you may grab the latest UMD build from unpkg (either a development or a production build). The UMD build exports a global called window.ReduxDefine if you add it to your page via a <script> tag.

We don’t recommend UMD builds for any serious application, as most of the libraries complementary to Redux are only available on npm.

Usage

defineAction(type, ?[subactions], ?namespace)

import { defineAction } from 'redux-define';

Create a redux action type with one or more subactions:

const CREATE_TODO = defineAction('CREATE_TODO', ['ERROR', 'SUCCESS']);

// result:
console.log('' + CREATE_TODO);            // CREATE_TODO
console.log('' + CREATE_TODO.ERROR);      // CREATE_TODO_ERROR
console.log('' + CREATE_TODO.SUCCESS);    // CREATE_TODO_SUCCESS;

Namespaces can be used to separate actions through out modules and apps.

const CREATE_TODO = defineAction('CREATE_TODO', ['ERROR', 'SUCCESS'], 'my-app');

// result:
console.log('' + CREATE_TODO);            // my-app/CREATE_TODO
console.log('' + CREATE_TODO.ERROR);      // my-app/CREATE_TODO_ERROR
console.log('' + CREATE_TODO.SUCCESS);    // my-app/CREATE_TODO_SUCCESS;

It's also possible to give in another constant as namespace for the new one.

const todos = defineAction('todos', ['LOADING', 'SUCCESS'], 'my-app');
const CREATE_TODO = defineAction('CREATE_TODO', ['ERROR', 'SUCCESS'], todos);

// result:
console.log('' + CREATE_TODO);            // my-app/todos/CREATE_TODO
console.log('' + CREATE_TODO.ERROR);      // my-app/todos/CREATE_TODO_ERROR
console.log('' + CREATE_TODO.SUCCESS);    // my-app/todos/CREATE_TODO_SUCCESS;

To integrate better with other redux libraries, a special ACTION property is added to the constant. redux-actions and redux-saga for example treat actionTypes other than string specially.

Extra benefit of this little feature, is that it makes the separation between user actions and status updates more clear. Read more about this under best practice and integrations

const CREATE_TODO = defineAction('CREATE_TODO', ['ERROR', 'SUCCESS']);

// result:
console.log('' + CREATE_TODO);            // CREATE_TODO
console.log('' + CREATE_TODO.ACTION);     // CREATE_TODO
console.log('' + CREATE_TODO.ERROR);      // CREATE_TODO_ERROR
console.log('' + CREATE_TODO.SUCCESS);    // CREATE_TODO_SUCCESS;

actionType.defineAction(type, ?[subactions])

As alternative syntax, we can use the defineAction method on defined constants. Constants defined in this way inherit their namespace. Making the namespace argument obsolete.

const myApp = defineAction('my-app');
const todos = myApp.defineAction('todos', ['LOADING', 'SUCCESS']);
const CREATE = todos.defineAction('CREATE', ['ERROR', 'SUCCESS']);

This is the same as writing:

const myApp = defineAction('my-app');
const todos = defineAction('todos', ['LOADING', 'SUCCESS'], 'my-app');
const CREATE = todos.defineAction('CREATE', ['ERROR', 'SUCCESS'], todos);

Or if you only need the CREATE constant:

const CREATE = todos.defineAction('CREATE', ['ERROR', 'SUCCESS'], 'my-app/todos');

Result in these cases is the same. Except in the third case, where we only defined the CREATE constant:

console.log('' + myApp);                  // my-app

console.log('' + todos);                  // my-app/todos
console.log('' + todos.LOADING);          // my-app/todos_LOADING
console.log('' + todos.SUCCESS);          // my-app/todos_SUCCESS

console.log('' + CREATE);                 // my-app/todos/CREATE
console.log('' + CREATE.ERROR);           // my-app/todos/CREATE_ERROR;
console.log('' + CREATE.SUCCESS);         // my-app/todos/CREATE_SUCCESS;

Best practice

Extract general state constants into a separate file so they can easily be imported and shared across different modules:

// stateConstants.js
export const LOADING = 'LOADING';
export const ERROR = 'ERROR';
export const SUCCESS = 'SUCCESS';
// app.js
export const myApp = defineAction('my-app');

In the module; we can import the stateConstants and optionally parent modules to construct a namespace.

// todos.js
import { defineAction } from 'redux-define';
import { LOADING, ERROR, SUCCESS } from './stateConstants';
import { myApp } from './app';

const todos = defineAction('todos', [LOADING, SUCCESS], myApp);
const CREATE = defineAction('CREATE', [ERROR, SUCCESS], todos);

// result:
console.log('' + myApp);                  // my-app

console.log('' + todos);                  // my-app/todos
console.log('' + todos.LOADING);          // my-app/todos_LOADING
console.log('' + todos.SUCCESS);          // my-app/todos_SUCCESS

console.log('' + CREATE);                 // my-app/todos/CREATE
console.log('' + CREATE.ACTION);          // my-app/todos/CREATE
console.log('' + CREATE.ERROR);           // my-app/todos/CREATE_ERROR
console.log('' + CREATE.SUCCESS);         // my-app/todos/CREATE_SUCCESS

Use the ACTION constant in dispatch and in saga watchers. This makes it clear that an user or system ACTION is being handled. All other subtypes should be status updates. They should be handled trough thunks or sagas, but never dispatched by a user. Although it is possible to handle user actions in the reducer directly, the advice is to not do this. Keep clear separation between user actions and reducer actions.

Implementation example

stateConstants.js
export const CANCELLED = 'CANCELLED';
export const ERROR     = 'ERROR';
export const PENDING   = 'PENDING';
export const SUCCESS   = 'SUCCESS';
actionTypes.js
import { defineAction } from 'redux-define';
import { CANCELLED, ERROR, PENDING, SUCCESS } from './stateConstants';

export const DELETE_COMMENT = defineAction('DELETE_COMMENT',
	[CANCELLED, ERROR, PENDING, SUCCESS], 'comments');
actions.js
import { createAction } from 'redux-actions';
import { DELETE_COMMENT } from './actionTypes';

export const deleteComment = createAction(DELETE_COMMENT.ACTION);
reducer.js
import { handleActions, combineActions } from 'redux-actions';
import { DELETE_COMMENT } from './actionTypes';

const initialState = {
  isDeleting: false,
};

const reducer = handleActions({
  [DELETE_COMMENT.PENDING]: state => ({
    ...state,
    isDeleting: true,
  }),

  [combineActions(
    DELETE_COMMENT.CANCELLED,
    DELETE_COMMENT.SUCCESS,
    DELETE_COMMENT.ERROR,
  )]: state => ({
    ...state,
    isDeleting: false,
  }),
}, initialState);
sagas.js
import { call, put, take } from 'redux-saga/effects';
import deleteAPI from 'somewhere-out-of-this-scope';
import { DELETE_COMMENT } from './actionTypes';

export function* deleteComment({ payload }) {
  try {
    yield put({ type: DELETE_COMMENT.PENDING });
    const { data } = yield call(deleteAPI, payload);
    yield put({ type: DELETE_COMMENT.SUCCESS, payload: data });
  }
  catch (error) {
    yield put({ type: DELETE_COMMENT.ERROR, payload: { error: error.message } });
  }
}
watchers.js
import { takeEvery } from 'redux-saga';
import { fork } from 'redux-saga/effects';

import { DELETE_COMMENT } from './actionTypes';
import * as s from './sagas';

function* deleteCommentWatcher() {
  yield* takeEvery(DELETE_COMMENT.ACTION, s.deleteComment);
}

export default function* () {
  yield [
    fork(deleteCommentWatcher),
  ];
}

Why use redux-define?

This library reduces a lot of the boilerplate that comes with defining redux action types. This library is created as solution to organizing large ducks Let's show the difference here. See above for a full implementation example. When using ducks, some of the files in the example above should be joined into a single duck file.

Without using redux-define

const CREATE_TODO = 'CREATE_TODO';
const CREATE_TODO_PENDING = 'CREATE_TODO_PENDING';
const CREATE_TODO_ERROR = 'CREATE_TODO_ERROR';
const CREATE_TODO_SUCCESS = 'CREATE_TODO_SUCCESS';

const DELETE_TODO = 'DELETE_TODO';
const DELETE_TODO_PENDING = 'DELETE_TODO_PENDING';
const DELETE_TODO_CANCELLED = 'DELETE_TODO_CANCELLED';
const DELETE_TODO_ERROR = 'DELETE_TODO_ERROR';
const DELETE_TODO_SUCCESS = 'DELETE_TODO_SUCCESS';

With redux-define

import { defineAction } from 'redux-define';
import { PENDING,  CANCELLED, ERROR, SUCCESS } from '/lib/stateConstants.js';

const CREATE_TODO = defineAction('CREATE_TODO', [PENDING, ERROR, SUCCESS]);
const DELETE_TODO = defineAction('DELETE_TODO', [PENDING, CANCELLED, ERROR, SUCCESS]);

Integrations

Created constants can be directly used in sagas reducers, or together with redux-actions.

See implementation example in this readme for implementation details. We handle redux-actions in actions.js and reducer.js and redux-saga in watchers.js and sagas.js.

Keywords

FAQs

Package last updated on 01 Jun 2017

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc