1.0.3 • Published 2 years ago

@react-things/use-reducer-actions v1.0.3

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

useReducerActions

useReducer with actions

npm npm bundle size

Install

npm install @react-things/use-reducer-actions
yarn add @react-things/use-reducer-actions

Usage

import { useReducerActions } from '@react-things/use-reducer-actions';

const Counter = () => {
  const [
    count,
    { increment, decrement, incrementByAmount },
  ] = useReducerActions({
    initialState: 0,
    reducers: {
      increment: state => state + 1,
      decrement: state => state - 1,
      incrementByAmount: (state, amount: number) => state + amount,
    },
  });

  return (
    <div>
      <div>
        <button onClick={decrement}>-</button>
        <div data-testid="count">{count}</div>
        <button onClick={increment}>+</button>
      </div>
      <div>
        <button onClick={() => incrementByAmount(3)}>Increment by 3</button>
      </div>
    </div>
  );
};
import { useReducerActions } from '@react-things/use-reducer-actions';

type Todo = {
  id: string;
  text: string;
  isDone: boolean;
};

const Todos = () => {
  const [todos, { addTodo }] = useReducerActions({
    initialState: [] as Todo[],
    reducers: {
      addTodo: (state, todo: Todo) => [...state, todo],
    },
  });

  return (
    <div>
      <ul>
        {todos.map(todo => {
          return (
            <li key={todo.id}>
              {todo.text}
              {todo.isDone && '✅'}
            </li>
          );
        })}
      </ul>
      <button
        onClick={() => {
          addTodo({ id: '1', text: 'New todo', isDone: false });
        }}
      >
        Add todo
      </button>
    </div>
  );
};

ℹ️ The actions object you get back from useReducerActions is memoized (it never changes it's reference)

Examples

This library goes nicely with the library constate, which is a helper for creating context.

import { useReducerActions } from '@react-things/use-reducer-actions';
import constate from 'constate';

type Book {
  id: string;
  title: string;
  author: string;
  numberInStock: number;
}

const useBooks = () => {
  const [books, bookActions] = useReducerActions({
    initialState: [],
    reducers: {
      sellBook: (books, id: string) => books.map(book => {
        if(book.id === id) return {...book, numberInStock: book.numberInStock - 1}
        return book;
      })
    }
  })
  return [books, bookActions]
}

// actions are memoized, so useBookActions will never cause a render!
const [BookProvider, useBooksState, useBookActions] = constate(useBooks, v => v[0], v => v[1])

const App = () => {
  return (
    <BookProvider>
      <Books />
    </BookProvider>
  )
}

const Books = () => {
  const books = useBookState();

  // ... render books
}