Because I lost my 2 factor authentic token, this project has moved to https://github.com/zhouhanseng/rechyons
Redux has one disadvantage: it is painfully verbose. Each time we want to add a feature, we needed to type a lot of lines for
- constants
- action types,
- action creators,
- action handlers in the reducer
- ...
Actually we bear this disadvantage for several years, and the situation seems to be getting worse. See verbose nightmare
It's time to rethink how to use redux. Why you need no Redux Saga
Rechyons makes redux state easy to update and retrieve like normal js object,meanwhile keep its immutable.
// update
hyperstore.user.update({ name: "yourname" });
// retrieve
let username = hyperstore.user.name;
Example or a little bigger app
$ git clone git@github.com:ZhouHansen/rechyons.git
$ cd rechyons/example
$ yarn install
$ yarn start
Support both typescript and javascript
$ npm install rechyons
// your app tsconfig.json
{
"compilerOptions": {
"strict": false
}
}
rechyons
generate action and reducer for each of the state fields. Shape your initState's structure to this:
{
moduleA: {keyA: somevalue, keyB: somevalue, keyC: somevalue},
moduleB: {keyA: somevalue, keyB: somevalue, keyC: somevalue},
}
rechyons
exports two functions rechyons.reducer()
and rechyons()
.
rechyons.reducer()
takes your init state to generate 'user/name'
, 'user/age'
, 'animal/category'
, 'animal/weight'
four pair of action and reducer automatically. Then return the reducers to redux.combineReducers
to create the store.
rechyons()
swallows store.dispatch
for calling the designated actions.
// store.ts
import { createStore, combineReducers } from "redux";
import rechyons from "rechyons";
let initState = {
// moduleA
user: {
name: "zhc",
age: 10,
},
// moduleB
animal: {
category: "dog",
weight: 10,
},
};
export let store = createStore(combineReducers(rechyons.reducer(initState)));
export default rechyons(initState, store.dispatch);
rechyons(initState, store.dispatch)
returns a hyperstore
, hyperstore.user
and hyperstore.animal
both are instances of Rechyons
class.
// TestComponent
import React from "react";
import { connect } from "react-redux";
import hyperstore from "./store";
export interface Props {
name: string;
}
class TestComponent extends React.Component<Props, {}> {
constructor(props: Props) {
super(props);
}
render() {
return (
<div>
<button
data-testid="button"
onClick={() => {
hyperstore.user.update("name", "abc");
}}
>
{this.props.name}
</button>
</div>
);
}
}
const MapStateToProps = (store) => {
return {
name: store[hyperstore.user.name],
};
};
export default connect(MapStateToProps)(TestComponent);
store[hyperstore.user.name]
equals to initState.user.name
which is "zhc";
store[hyperstore.animal.weight]
equals to initState.animal.weight
which is 10; So we can use this to MapStateToProps()
Use hyperstore.user.update("name", "abc")
or hyperstore.user.update({"name": "abc"})
,
hyperstore.user.update("name", "abc")
对特指的 action 执行了store.dispatch({type: "user/name", "abc})
type ReducerType = { [key: string]: (state: any, action: AnyAction) => any };
type initStateType = { [key: string]: { [key: string]: any } };
rechyons.reducer: (initState: initStateType) => ReducerType
rechyons.reducer()
return the reducers generated from initstate, so it only devotes to create redux store.
export let store = createStore(combineReducers(rechyons.reducer(initState)));
rechyons: (initState: initStateType, dispatch: Dispatch<AnyAction>) => { [key: string]: Rechyons }
Each hyperstore.someModule
is a Rechyons instance
import hyperstore, { store } from "./store";
let hyperstore = rechyons(initState, store.dispatch);
// hyperstore.user is one of the Rechyons instances
console.log(hyperstore.user.name); // get the keyname output "user/name"
console.log(store[hyperstore.user.name]); // get the value output "zhc"
hyperstore.user.update({ name: "abc", age: 20 }); // change state
console.log(hyperstore.user.name); // output "abc"
I want to add a like feature ❤ on the image people post in a social app like twitter.
export default {
state: {
//...
},
effects: {
//... Thousands of lines
*toggleLike({ payload }, { call, put }) {
const { isLiked, id } = payload;
if (isLiked) {
yield call(services.setLike, id);
} else {
yield call(services.setUnLike, id);
}
yield put(toggleLikeSuccess({ id, isLiked }));
},
},
reducer: {
//...
},
};
In models/somemodule.js
, add a generator to commit actions in effects
object.
export function toggleLikeSuccess({ id, isLiked }) {
return {
type: "toggleLikeSuccess",
payload: {
id,
isLiked,
},
};
}
In actions/somemodule.js
, define a new action.
export default {
state: {
//...
},
effects: {
//... Thousands of lines
},
reducer: {
//...Thousands of lines
toggleLikeSuccess(state, { payload }) {
const { id, isLiked } = payload;
return {
...state,
list: list.map((item) => {
if (item.id === id) {
const newLikeNum = isLiked ? item.like_num + 1 : item.like_num - 1;
return {
...item,
is_liked: isLiked,
like_num: newLikeNum > 0 ? newLikeNum : 0,
};
}
return item;
}),
};
},
},
};
In models/somemodule.js
, add a reducer in reducer
object.
const mapDispatchToProps = (dispatch) => ({
dispatch,
toggleLikeMyImgTxt: compose(
dispatch,
// ...
actions.triggerAction("somemodule/toggleLike")
),
});
In components/somecomponent.js
, map dispatch
to props
.
Life is too heavy