Skip to content

Latest commit

 

History

History
312 lines (239 loc) · 7.88 KB

README-ZH.md

File metadata and controls

312 lines (239 loc) · 7.88 KB

Rechyons

stability npm travis dm js-standard-style

Redux 不再冗长啰嗦, Redux 伟大复兴!

有这样一个库让你在使用redux,无需再写reducers和actions,同时保持不可突变性

迁移

因为我丢失了我的 2fa,这个项目搬迁到了 https://github.com/zhouhanseng/rechyons

动机

重复大量地命名真是很烦人 Redux 有一个缺点: 它太啰嗦了,当你需要加一个小功能时,就要写很多行:

  • 常量名
  • action 类型,
  • action 创建者,
  • reducer,在 reducer 中执行 action
  • ...

实际上我们已经忍受这个缺点好几年了, 同时情况似乎变得越来越糟糕,当我们不加思考地滥用 redux sagas 和 generator 时,便陷入了冗长的噩梦

是时候重新思考如何使用 redux 了。 为什么你不需要 Redux Saga

通过rechyons, 你再也不用写上述的这一切了

Rechyons 让 redux state 变得像一般 js object 一样容易修改和取值,同时保持它的 immutable。

// 修改
hyperstore.user.update({ name: "yourname" });

// 取值
let username = hyperstore.user.name;

使用

例子 或者 稍大一点的应用

$ git clone git@github.com:ZhouHansen/rechyons.git
$ cd rechyons/example
$ yarn install
$ yarn start

原理 (8 分钟差不多读完, 比你想象简单)

安装

支持 typescript 和 javascript

$ npm install rechyons

前提

// 你的app的tsconfig.json
{
  "compilerOptions": {
    "strict": false
  }
}

启动 redux store 和 rechyons

rechyons 为每一个 state 数据生成 action 和 reducer。你的 State 的结构需要是这样的:

{
  moduleA: {keyA: somevalue, keyB: somevalue, keyC: somevalue},
  moduleB: {keyA: somevalue, keyB: somevalue, keyC: somevalue},
}

rechyons 出口两个函数 rechyons.reducer()rechyons().

rechyons.reducer() 用你的初始 state 自动生成 'user/name', 'user/age', 'animal/category', 'animal/weight' 四对 action 和 reducer。 然后返回 reducers 到 redux.combineReducers来创建 store.

rechyons() 吞下 store.dispatch 为接下来提交自生成的 action。

// store.ts
import { createStore, combineReducers } from "redux";

import rechyons from "rechyons";

let initState = {
  // moduleA
  user: {
    name: "小成",
    age: 10,
  },
  // moduleB
  animal: {
    category: "猫咪",
    weight: 10,
  },
};

export let store = createStore(combineReducers(rechyons.reducer(initState)));

export default rechyons(initState, store.dispatch);

在组件中进行数据绑定,获取和修改

rechyons(initState, store.dispatch)返回一个hyperstore, hyperstore.userhyperstore.animal都是Rechyons的实例.

// 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", "小汉");
          }}
        >
          {this.props.name}
        </button>
      </div>
    );
  }
}

const MapStateToProps = (store) => {
  return {
    name: store[hyperstore.user.name],
  };
};

export default connect(MapStateToProps)(TestComponent);

从 state 中获取数据

store[hyperstore.user.name] 等于 initState.user.name 等于 "小成"; store[hyperstore.animal.weight] 等于 initState.animal.weight 等于 10; 所以我们用这个来进行MapStateToProps()

修改数据

使用 hyperstore.user.update("name", "小汉") 或者 hyperstore.user.update({"name": "小汉"})hyperstore.user.update("name", "abc") 对特指的 action 执行了store.dispatch({type: "user/name", "abc})

API

rechyons.reducer()

type ReducerType = { [key: string]: (state: any, action: AnyAction) => any };
type initStateType = { [key: string]: { [key: string]: any } };

rechyons.reducer: (initState: initStateType) => ReducerType

rechyons.reducer() 返回从初始 state 生成的 reducers,它的唯一作用是创建 redux store。

export let store = createStore(combineReducers(rechyons.reducer(initState)));

rechyons()

rechyons: (initState: initStateType, dispatch: Dispatch<AnyAction>) => { [key: string]: Rechyons }

每一个 hyperstore,someModule 都是一个 Rechyons 实例

import hyperstore, { store } from "./store";
let hyperstore = rechyons(initState, store.dispatch);

// hyperstore.user是一个Rechyons实例
console.log(hyperstore.user.name); // keyname 等于为 "user/name"
console.log(store[hyperstore.user.name]); // 通过keyname获得的值为 "小成"

hyperstore.user.update({ name: "小汉", age: 20 }); // 修改数据
console.log(hyperstore.user.name); // 输出 "小汉"

冗长的噩梦

我想加一个点赞功能 ❤️ 在一款社交 app 上类似微信。

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: {
    //...
  },
};

models/somemodule.jseffects对象中定义一个 generator 用于提交 action。

export function toggleLikeSuccess({ id, isLiked }) {
  return {
    type: "toggleLikeSuccess",
    payload: {
      id,
      isLiked,
    },
  };
}

actions/somemodule.js,定义一个新的 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;
        }),
      };
    },
  },
};

models/somemodule.jsreducerobject 对象中加一个 reducer。

const mapDispatchToProps = (dispatch) => ({
  dispatch,
  toggleLikeMyImgTxt: compose(
    dispatch,
    // ...
    actions.triggerAction("somemodule/toggleLike")
  ),
});

components/somecomponent.js,映射dispatchprops.

人艰不拆

License

MIT