npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ejoy-define

v1.1.1

Published

Define action constants for Redux

Downloads

5

Readme

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.