Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/elderfo/use-dispatch-action
https://github.com/elderfo/use-dispatch-action
Last synced: 21 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/elderfo/use-dispatch-action
- Owner: elderfo
- License: mit
- Created: 2021-01-06T02:04:20.000Z (almost 4 years ago)
- Default Branch: main
- Last Pushed: 2021-01-19T03:31:01.000Z (almost 4 years ago)
- Last Synced: 2024-10-03T22:41:03.932Z (about 1 month ago)
- Language: TypeScript
- Size: 474 KB
- Stars: 0
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# use-dispatch-action
Typed utilities for improving the experience with `useReducer`.
![npm](https://img.shields.io/npm/v/use-dispatch-action)
![GitHub Workflow Status](https://img.shields.io/github/workflow/status/elderfo/use-dispatch-action/CI)
![node-current](https://img.shields.io/node/v/use-dispatch-action)
![npm bundle size](https://img.shields.io/bundlephobia/min/use-dispatch-action)
![NPM](https://img.shields.io/npm/l/use-dispatch-action)## Problem
When using `useReducer`
- Dispatching actions are not type safe
- Action creators, while testable, introduce additional boilerplate code
- Changing an action type can lead to a reducer to no longer work properly, if you don't have tests## Solution
`use-dispatch-action` is a collection of utilities to improve the experience when using the `useReducer` hook.
## Getting started
Install
```bash
yarn add use-dispatch-action
```Or
```bash
npm install use-dispatch-action
```## Usage
```typescript
import * as React from 'react';
import { useDispatchAction } from 'use-dispatch-action';type Actions =
| { type: 'increment' }
| { type: 'decrement' }
| { type: 'addValue'; payload: number };type State = { counter: number };
const reducer = (state: State, action: Actions): State => {
switch (action.type) {
case 'increment':
return { ...state, counter: state.counter + 1 };
case 'decrement':
return { ...state, counter: state.counter - 1 };
case 'addValue':
return { ...state, counter: state.counter + action.payload };
default:
return state;
}
};const Component = () => {
const [state, dispatch] = React.useReducer(reducer, { counter: 0 });
const increment = useDispatchAction(dispatch, 'increment');
const decrement = useDispatchAction(dispatch, 'decrement');
const addValue = useDispatchAction(dispatch, 'addValue');return (
{state.counter}
Increment
Decrement
addValue(2)}>Add Two
);
};
```# API
## type `Action`
A utilty type for defining actions
```typescript
type Action = {
type: TActionType;
payload?: TPayload;
};
```### Example
```typescript
type Actions = Action<'incrementOne'> | Action<'increment', number>;
```## `useDispatchAction`
Creates type safe dispatch functions for the specified action
```typescript
useDispatchAction(
dispatch: React.Dispatch,
action: string
) : DispatchAction
```### Arguments
- `dispatch: React.Dispatch` - A dispatch method retured from `React.useReducer`
- `action: string` - The type of the action### Returns
- `DispatchAction` - Function to dispatch action
```typescript
// For actions without a payload
() => void;
// For actions with a payload
(payload: TPayload) => void;
```### Example ([types/reducer](#types-and-reducer-for-examples))
```typescript
const Component = () => {
const [state, dispatch] = React.useReducer(reducer, { counter: 0 });
const increment = useDispatchAction(dispatch, 'increment');
const decrement = useDispatchAction(dispatch, 'decrement');
const addValue = useDispatchAction(dispatch, 'addValue');return (
{state.counter}
increment()}>Increment
decrement()}>Decrement
addValue(2)}>Add Two
);
};
```## `useDispatchReducer`
Creates a reducer with a type safe dispatch method
```typescript
useDispatchReducer (
reducer: React.Reducer,
initialState: TState
) : [state: TState, ActionDispatcher]
````TState` and `TAction` can be infered by providing the type of a reducer.
```typescript
useDispatchReducer(
reducer: TReducer,
initialState: TState
) : [state: TState, ActionDispatcher]
```### Arguments
- `reducer: React.Reducer` - The reducer
- `initialState: TState` - State to initialize the reducer with. A note, `useDispatchReducer` does not implement lazy loading the state### Returns
A tuple with:
- `state: TState` - State of the reducer
- `ActionDispatcher` - Function to dispatch actions in the form of tuples
```typescript
// For actions without a payload
([type: string]) => void;
// For actions with a payload
([type: string, payload: TPayload]) => void;
```### Examples ([types/reducer](#types-and-reducer-for-examples))
With type inference
```typescript
const Component = () => {
const [state, dispatch] = useDispatchReducer(reducer, { counter: 0 });
const increment = () => dispatch(['increment']);
const decrement = () => dispatch(['decrement']);
const addValue = (number: number) => dispatch(['addValue', number]);return (
{state.counter}
Increment
Decrement
addValue(2)}>Add Two
);
};
```With known State and Action types
```typescript
const Component = () => {
const [state, dispatch] = useDispatchReducer(reducer, {
counter: 0,
});
const increment = () => dispatch(['increment']);
const decrement = () => dispatch(['decrement']);
const addValue = (number: number) => dispatch(['addValue', number]);return (
{state.counter}
Increment
Decrement
addValue(2)}>Add Two
);
};
```Only know the State type? The Action type can be inferred from the reducer as long as the actions are typed.
```typescript
const Component = () => {
const [state, dispatch] = useDispatchReducer(reducer, { counter: 0 });
const increment = () => dispatch(['increment']);
const decrement = () => dispatch(['decrement']);
const addValue = (number: number) => dispatch(['addValue', number]);return (
{state.counter}
Increment
Decrement
addValue(2)}>Add Two
);
};
```## DispatchContext
A context based dispatcher used to prevent prop drilling
```typescript
export type DispatchContextProps = {
initialState: TState;
reducer: React.Reducer;
};
```### props
- `initialState: TState` - state to initialize the reducer with
- `reducer: React.Reducer` - The reducer### Examples ([types/reducer](#types-and-reducer-for-examples))
Using a consumer
```typescript
const DispatchContext = () => {
return (
{({ state, dispatch }: DispatchProps) => (
{state.counter}
dispatch(['increment'])}>Increment
dispatch(['decrement'])}>Decrement
dispatch(['addValue', 2])}>Add Two
)}
);
};
```Using `useDispatchContext`
```typescript
const Component = () => {
return (
);
};const Counter = () => {
const [state, dispatch] = useDispatchContext();return (
{state.counter}
dispatch(['increment'])}>Increment
dispatch(['decrement'])}>Decrement
dispatch(['addValue', 2])}>Add Two
);
};
```## Types and reducer for examples
```typescript
type Actions =
| { type: 'increment' }
| { type: 'decrement' }
| { type: 'addValue'; payload: number };type State = { counter: number };
const reducer = (state: State, action: Actions): State => {
switch (action.type) {
case 'increment':
return { ...state, counter: state.counter + 1 };
case 'decrement':
return { ...state, counter: state.counter - 1 };
case 'addValue':
return { ...state, counter: state.counter + action.payload };
default:
return state;
}
};
```