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

use-case-reducers

v0.0.9

Published

A simplified version of React's useReducer, use this package to generate all actions automatically.

Downloads

4

Readme

Use Case Reducers

npm

useCaseReducers simplifies the work when you are using React's useReducer. Its api is almost the same as useReducer, so there are just a few things that you need to learn if you have already been familiar with useReducer.

Get started

npm install use-case-reducers
#or
yarn add use-case-reducers

Why use this package

Although useReducer is great, writing a reducer is kind of annoying, especially when we need to handle more actions. Suppose we need to handle 10 actions with our state, then we need to write 10 switch/case of 10 if/else to deal with these actions. Sounds terrible, right?

Furthermore, when we use useReducer, we probably don't want to dispatch an action by writing dispatch({type: 'addTodo', payload: newTodo}). The common use case we prefer may be writing an action creator for each action. For example, we may write:

// An action creator returns the action of adding a to-do
const addTodo = newTodo => ({ type: 'addTodo', payload: newTodo });
// Dispatch a action of adding a to-do by passing a action creator
dispatch(addTodo(newTodo));

Action creators help us writing cleaner code. But again, what if we need to handle so many actions? We definitely don't want to write these action creators manually, right?

useCaseReducers comes to the rescue! With useCaseReducers, we don't need to write a lot of switch/case and a lot of action creators. All we need to do is passing an object of case reducers, then useCaseReducers will generate a reducer and all action creators automatically.

What is a case reducer

The difference between a case reducer and a normal reducer is that a case reducer only handles one action while a normal reducer handles all actions. For example, if we use a reducer to handle a counter state, we may write:

const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return state + 1;
    case 'decrement':
      return state - 1;
    case 'add':
      return state + action.payload;
    case 'sub':
      return state - action.payload;
  }
};

We can split the above reducer into 4 case reducers:

const increment = state => state + 1;
const decrement = state => state - 1;
const add = (state, amount) => state + amount;
const sub = (state, amount) => state - amount;

As you can see, writing a case reducer is very easy.

API Reference

This package uses Immer, so you can mutate the state in your case reducers.

useCaseReducers

import useCaseReducers from 'use-case-reducers';

const [state, dispatch, actions] = useCaseReducers(caseReducers, initialArg, init);

The differences between useCaseReducers and useReducer are the first parameter and there is a third returned value in useCaseReducers. Instead of passing a normal reducer to the first parameter, useCaseReducers accepts an object which contains all case reducers. The third returned value is an object which contains all action creators generated by the caseReducers you pass in. Here is an example of how to use it:

const initialState = { count: 0 };
const caseReducers = {
  increment: state => {
    state.count += 1;
  },
  decrement: state => {
    state.count -= 1;
  },
  add: (state, amount) => {
    state.count += amount;
  },
  sub: (state, amount) => {
    state.count -= amount;
  },
};

const Counter = () => {
  const [{ count }, dispatch, { increment, decrement, add, sub }] = useCaseReducers(
    caseReducers,
    initialState
  );

  return (
    <div>
      count: {count}
      <button onClick={() => dispatch(increment())}>+</button>
      <button onClick={() => dispatch(decrement())}>-</button>
      <button onClick={() => dispatch(add(10))}>add 10</button>
      <button onClick={() => dispatch(sub(10))}>minus 10</button>
    </div>
  );
};

Lazy initialization

Like useReducer, you can also create the initial state lazily by providing an init function as the third parameter. The initial state will be set to init(initialArg).

The parameters for a case reducer

A case reducer can accepts an arbitrary number of parameters. Note that if a case reducer accepts more than one parameter, the first parameter is the state, and the rest parameters are the payload of a action. Here is an example:

const caseReducers = {
  // zero parameter
  reset: () => {
    return { count: 0 };
  },
  // one parameter, that is, it only accepts state
  increment: state => {
    state.count += 1;
  },
  // two parameters, the second is the payload
  add: (state, amount) => {
    state.count += amount;
  },
  // the number of the payload parameters is not constrained,
  // that is, you can specify an arbitrary number of parameters to be the payload
  addTwo: (state, amount1, amount2) => {
    state.count += amount1 + amount2;
  },
};

createCaseReducers

import { createCaseReducers } from 'use-case-reducers';

const { initialState, caseReducers } = createCaseReducers(_initialState, _caseReducers);

If you are a typescript user, write a plain object of case reducers may be verbose. For example, if we want to write an object of case reducers to handle a state whose type is number, we should write something like the following code:

const caseReducers = {
  increment: (state: number) => state + 1,
  decrement: (state: number) => state - 1,
  add: (state: number, amount: number) => state + amount,
  sub: (state: number, amount: number) => state - amount,
};

As you can see, although the type of the state is the same, we need to specify its type for every case reducer.

An alternative solution is to use createCaseReducers. It can generate a well type defined object without specifing the type of state for every case reducer. Here is an example of how to use createCaseReducers:

const { initialState, caseReducers } = createCaseReducers(0, {
  increment: state => state + 1,
  decrement: state => state - 1,
  add: (state, amount: number) => state + amount,
  sub: (state, amount: number) => state - amount,
});

Note that this function just simply returns the _initialState and _caseReducers you pass in. It can be helpful when you are using typescript.

createSlice

import { createSlice } from 'use-case-reducers';

const { initialState, reducer, actions } = createSlice(_initialState, caseReducers);

If you want to use React's useReducer directly, then this function may be helpful for you. It will generate a reducer and all actions creators you need. Here is an example of how to use:

const {
  initialState,
  reducer,
  actions: { increment, decrement },
} = createSlice(0, {
  increment: state => state + 1,
  decrement: state => state - 1,
});

const App = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <div>{state}</div>
      <button onClick={() => dispatch(increment())}>+</button>
      <button onClick={() => dispatch(decrement())}>-</button>
    </div>
  );
};

createActions

import { createActions } from 'use-case-reducers';

const actions = createActions(caseReducers);

If you only want to use action creators from your caseReducers, you can pass it to this function, then this function will return an object contains all action creators.

createReducer

import { createReducer } from 'use-case-reducers';

const reducer = createReducer(caseReducers);

This function will return a reducer function generated by the caseReducers you pass in.

dispatch

import { dispatch } from 'use-case-reducers';

const DispatchContext = React.createContext(dispatch);

This function is useful when you want to provide a default context because its type is the same as useCaseReducers and useReducer's returned dispatch.

If you don't have a provider, this function will throw an error.