https://github.com/ryym/redy
Yet another Redux wrapper
https://github.com/ryym/redy
react redux
Last synced: about 2 months ago
JSON representation
Yet another Redux wrapper
- Host: GitHub
- URL: https://github.com/ryym/redy
- Owner: ryym
- License: mit
- Created: 2018-12-20T00:17:23.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2020-03-09T08:46:00.000Z (about 5 years ago)
- Last Synced: 2024-04-23T22:09:54.459Z (about 1 year ago)
- Topics: react, redux
- Language: TypeScript
- Homepage:
- Size: 517 KB
- Stars: 0
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Redy
[](https://github.com/ryym/redy/actions?query=workflow%3ATest)
Redy is a yet another [Redux](https://redux.js.org/) wrapper to use Redux with ease.
Features:
- Type safe
- Less boilerplate## What it looks like
(See the next [Overview](#overview) section for the detailed usage)
### Store
Redy changes:
- how you define action creators
- how you define a reducer```typescript
import {createStore, applyMiddleware, Dispatch} from 'redux';
import {defineActions, effect, defineReducer, on, redyMiddleware, Thunk} from 'redy';export type State = {count: number};
// 1. Define action creators.
export const actions = defineActions('counter', {
Increment: (n: number) => n,Double: () => {},
IncrementAsync: effect(
({n, ms}: {n: number; ms: number}): Thunk => async (dispatch, state) => {
console.log('increment async', state().count);
setTimeout(() => dispatch(actions.Increment(n)), ms);
},
),
});// 2. Define a reducer.
const reducer = defineReducer({count: 0}, [
on(actions.Increment, ({count}, n) => {
return {count: count + n};
}),on(actions.Double, ({count}) => {
return {count: count * 2};
}),
]);// 3. Create a store.
export const store = createStore(reducer, applyMiddleware(redyMiddleware()));
```### Component
It has no differences with the normal Redux usage.
```typescript
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider, connect} from 'react-redux';
import {State, store, actions} from './store';type Props = {
count: number;
dispatch: Dispatch;
};const Counter = ({count, dispatch}: Props) => {
return (
count: {count}
dispatch(actions.Increment(3))}>
Increment 3
dispatch(actions.Double())}>
Double
dispatch(actions.IncrementAsync({n: 10, ms: 800}))}>
Increment Async
);
};const ConnectedCounter = connect((state: State) => state)(Counter);
ReactDOM.render(
,
document.getElementById('root'),
);
```## Overview
### Action creators
Redy lets you define action creators as a group using `deifneActions`.
```typescript
import {defineActions} from 'redy'const counterActions = defineActions('counter', {
Increment: (n: number) => n,
Double: () => {},
})console.log(counterActions.Increment(5))
//=> { type: 'counter/Increment', payload: 5 }console.log(counterActions.Increment.actionType)
//=> 'counter/Increment'
```The `defineActions` function converts each function to an action creator.
It determines the action type automatically from the function name.
Thus you don't need to define an action type explicitly.
Also Redy recommend not defining a union type of actions.
We think a reducer function could be type safe sufficiently without a union type.### Reducer
Redy provides `defineReducer` and `on` to create a reducer in type-safe manner without an action union type.
```typescript
import {defineReducer, on} from 'redy'
import {todoActions} from './actions'const initialState = []
export const reducer = defineReducer(initialState, [
on(todoActions.Add, (todos, { id, title }) => {
const todo = { id, title, completed: false }
return [...todos, todo]
}),on(todoActions.Remove, (todos, id) => {
return todos.filter(todo => todo.id !== id)
}),// on(actionCreator, (state, payload) => state)
])console.log(reducer(undefined, {type: '_init_'}))
//=> []console.log(reducer([], todoActions.Add(1, 'Buy milk')))
//=> [{id: 1, title: 'Buy milk'}]
```Additionally Redy provides `onAny` to handle multiple actions with a single handler.
```typescript
import {defineReducer, onAny} from 'redy'
import {actions} from './actions'defineReducer({}, [
onAny([actions.FetchFollowerDone, actions.FetchFolloweeDone], (users, {user}) => {
return {...users, [user.id]: user}
})
])
```### Effect
[redux-thunk]: https://github.com/reduxjs/redux-thunk
Redy uses the [redux-thunk][redux-thunk] pattern for effect handling.
```typescript
import {defineActions, effect, Thunk} from 'redy'
import {User, userApi} from './user'type State = {[id: number]: User}
const userActions = defineActions('users', {
Fetch: effect(
(id: number): Thunk => async (dispatch, getState) => {
const users = getState()
if (users[id] == null) {
const user = await userApi.findById(id)
dispatch(userActions.FetchOk(user))
}
}
),FetchOk: (user: User) => user,
// ...
})
```### Store
The only thing you need to do is applying `redyMiddleware`.
It handles the effect actions.```typescript
import {Store, applyMiddleware, createStore} from 'redux'
import {redyMiddleware} from 'redy'
import {reducer} from './reducer'
import {State} from './state'export const configureStore = (): Store => {
return createStore(reducer, applyMiddleware(redyMiddleware()))
}
```