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

redux-actioner

v1.0.2

Published

Make Redux actions simple, predictable, and maintainable.

Downloads

5

Readme

Redux Actioner

Make Redux actions simple, predictable, and maintainable.

Introduction

Creating actions should be easy and consistent. Inspired by Flux Standard Actions, this framework facilitates the implementation of Redux actions for the sake of simplicity, testing, and maintainability.

Installation

yarn add redux-actioner

Simplicity

Manage your actions like reducers. Redux Actioner provides createActionFactory which you can use to namespace your actions and add Action Slices that are broken down into three types: REQUEST, SUCCESS, and FAILURE.

{
  type: 'TODO/ADD/REQUEST',
  baseType: 'TODO/ADD',
  payload: {
    text: 'Do something great.'
  },
  actionType: 'REQUEST'
}

Here is our REQUEST action that is namespaced in the TODO factory > ADD slice. The baseType property contains the factory namespace and the slice name for the ease of handling in reducers.

Maintainability

Redux Actioner abstracts the frustration from creating objects and maintaining an application. Request action creators are reusable and easy to update when your API changes. Components remain pure of API versioning and integration with Redux Saga and Redux Observable is seamless.

Usage

Define Actions

import { createActionFactory, request } from 'redux-actioner';
const createSliceAction = createActionFactory('TODO');

export const ADD = createSliceAction('ADD', ({ text, title, listId: list_id })
  => ({ text, title, list_id }));

/*
ADD = {
  baseType: 'TODO/ADD',
  REQUEST: 'TODO/ADD/REQUEST',
  SUCCESS: 'TODO/ADD/SUCCESS',
  FAILURE: 'TODO/ADD/FAILURE',
  requestPayloadCreator: ({ text, title, listId: list_id })
    => ({ text, title, list_id }))
}
*/

In the example above we have created a namespace called "TODO" and added an action to it called "ADD." In the comment we can see the baseType is TODO/ADD which will make it easy to bind and reduce. The payload creator is included for creating request action types which makes for a predictable payload.

Bind to Dispatch

import { connect } from 'react-redux';
import { bindRequestActions } from 'redux-actioner';
import * as TodoActions from '../actions';

class TodoForm extends React.Component {
  state = { text: "Do something greatest.", title: "Tomorrow..." }

  onSubmit() {
    this.props.actions.addTodoItem({
      listId: this.props.listID,
      ...this.state.form
    });
  }

  render() {
    <Form onSubmit={() => this.onSubmit()} />
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    actions: bindRequestActions({
      addTodoItem: TodoActions.ADD
    }, dispatch);
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(TodoForm);

The example above will feel familiar with the only difference being that Actioner does not require action creators in your components. ActionSlice objects are given to bindRequestActions which will return an object of action creators similar to Redux's bindActionCreators. This keeps components clean of API changes and minimizes redundancies.

Reduce and Slice

import * as TodoActions from '../actions';

function todoReducer(state = { items: [], isSubmitting: null }, action) {
  const { baseType } = action;

  switch(baseType) {
    case TodoActions.ADD.BASE_TYPE:
      return addSlice(state, action);
    case TodoActions.REMOVE.BASE_TYPE:
      return removeSlice(state, action);
  }
}

function addSlice(state, action) {
  const { actionType, payload } = action;
  switch (actionType ) {
    case 'REQUEST':
      return { ...state, isSubmitting: true };
    case 'SUCCESS':
      const newTodo = { createdAt: new Date(), ...payload };
      return { isSubmitting: false, items: state.items.concat(newTodo) };
    break;
  }
}

In the example above you will see our todoReducer which has been abstracted into slices to deal with ActionSlice types. When working with large reducers and many actions it becomes a necessity to break out into slices. The end result is cleaner and easier to manage.

Action Context

When managing side effects it can be difficult to keep track of data as it flows from action to action. Maintaining context is important to ensure actions that respond to requests can resolve later on when reducing.

Payload data from a request action can be abstracted and dispatched in success or failure actions by providing context to success(actionSlice, context) or failure(actionSlice, context):

const requestAction = request(TodoActions.ADD)({ text: "Do something most great."});
const { payload } = requestAction;
const { text } = payload;
fetch(apiRequest)
  .then(response =>
    const successAction = success(TodoActions.ADD, { text })(response);
    store.dispatch(successAction);
  )
  .catch(error =>
    const errorAction = failure(TodoActions.ADD, { text })(error);
    store.dispatch(errorAction);
  );

Testing

By using standardized payload creators, action data becomes more predictable and easier to test.

The payload to a REQUEST action is reduced through a function to prepare the payload parameters for dispatch. It makes API changes flexible and easy to manage in action creators.

describe("TODO/ADD request action", () => {
  const payload = { text: 'Do something greater.', title: 'Today...', listId: 1 };

  it("converts from camel case to snake case", () => {
    expect(request(TodoActions.ADD)(payload))
      .toEqual(jasmine.objectContaining({
        text: payload.text,
        title: payload.title,
        list_id: payload.listId
      }));
  }
})

Manual

Contributors