Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rc-redux-model 介绍 #1

Open
PDKSophia opened this issue Aug 22, 2020 · 0 comments
Open

rc-redux-model 介绍 #1

PDKSophia opened this issue Aug 22, 2020 · 0 comments
Labels
docs 文档

Comments

@PDKSophia
Copy link
Collaborator

PDKSophia commented Aug 22, 2020

Why rc-redux-model ?

相信大家都了解 redux,并且也认同这种数据流的方式(毕竟不认同,你也不会用嘛~),然,世间万物,皆有利弊。

以我为例,每次起一个项目,我都需要 :

  • 脚手架 create-react-app 快速生成一个应用框架,进行开发
  • 安装 redux 进行数据状态管理
  • 安装 react-redux ,调用 Provider 提供者模式,使得子组件都能取到 store 值
  • 如果想要解决异步请求,我也许还需要安装一个 redux-saga / redux-thunk
  • 如果想看到日志,那么我还会安装 redux-logger
  • ...

看似一顿操作猛如虎,其实心中已经 MMP,我会想,这么多前置工作,是不是让我的开发成本更高了呢?

为了解决异步 Action,我需要按照 redux-saga 或者 redux-thunk,从而处理异步问题,以 redux-saga 为例 :

在使用中,我发现 redux + redux-saga 让我的 [重复性] 工作变多(逐步晋升 CV 工程师),因为它存在啰嗦的样板代码。

举个 🌰 : 异步请求,获取用户信息,我需要创建 sagas/user.jsreducers/user.jsactions/user.js,为了统一管理 const,我还会有一个 const/user.js,然后在这些文件之间来回切换。

分文件应该是一种默认的规范吧?反正我实习的时候,是分文件的;现在组里,是分文件的;看一些优秀库(star 多),也是分文件的;包括看 dva 的介绍,它也提到了问题

// const/user.js
const FETCH_USER_INFO = 'FETCH_USER_INFO'
const FETCH_USER_INFO_SUCCESS = 'FETCH_USER_INFO_SUCCESS'
// actions/user.js
export function fetchUserInfo(params, callback) {
  return {
    type: FETCH_USER_INFO,
    params,
    callback,
  }
}
// sagas/user.js
function* fetchUserInfoSaga({ params, callback }) {
  const res = yield call(fetch.callAPI, {
    actionName: FETCH_USER_INFO,
    params,
  })
  if (res.code === 0) {
    yield put({
      type: FETCH_USER_INFO_SUCCESS,
      data: res.data,
    })
    callback && callback()
  } else {
    throw res.msg
  }
}
// reducers/user.js
function userReducer(state, action) {
  switch (action.type) {
    case FETCH_USER_INFO_SUCCESS:
      return Immutable.set(state, 'userInfo', action.data)
  }
}

没错, 这种样板代码,简直就是 CV 操作,只需要 copy 一份,修改一下名称,对我个人而言,这会让我不够专注,分散管理 const、action、saga、reducer 一套流程,需要不断的跳跃思路。

而且文件数量会变多,我是真的不喜欢如此繁琐的流程,有没有好的框架能帮我把这些事都做完呢?

dva

世间万物存在,必然有它自身的价值和意义。dva 的出现,肯定是解决了一些问题。我们看看 dva 官网怎么说的 ~~

dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架。

有意思,但是因为 dva 身负重任,对我而言,使用它太过于“笨重”,我只是想取其精华,去其内置,我就只想用它写状态管理的方式: 在 model 里边,写完 reducer, state, action

再一次与 JPL 同学交流的过程中,发现他也有这种想法,同时他已经写了一个简单的中间件,在他们组里用了起来,出于学习以及如何写一个中间件,在参考它的代码之后,我也开始尝试写一个 redux 的中间件,并且内部支持默认的 action,让开发更加简洁,释放键盘上的 C 与 V ,同时对于 Immutable 的支持以及错误类型的检测,让你的 state 更加“合法”

于是 rc-redux-model 就这样出现了...

What's rc-redux-model ?

rc-redux-model 是一个中间件,提供一种更为简洁和方便的数据状态管理[书写方式]。参考了 dva 的数据流方案,在一个 model 文件中写所有的 actionreducerstate,解读了 redux-thunk 的源码,内部实现了一个中间价,同时提供默认行为 action,调用此 action 可以直接修改任意值的 state,例如 :

只需要定义一个 model

export default {
  namespace: 'reduxModel',
  state: {
    testA: '',
    testB: [],
  },
}

那么 rc-redux-model 会自动帮你注册 action 及 reducers,等价于 action 和 reducers 不用你自己写了,如下 :

export default {
  namespace: 'reduxModel',
  state: {
    testA: '',
    testB: [],
  },
  action: {
    settestA: ({ commit, currentAction }) => {
      commit({
        type: 'SET_REDUXMODEL_TESTA',
        payload: currentAction.payload,
      })
    },
    settestB: ({ commit, currentAction }) => {
      commit({
        type: 'SET_REDUXMODEL_TESTB',
        payload: currentAction.payload,
      })
    },
    // 推荐使用此action进行修改reducers值
    setStore: ({ dispatch, currentAction }) => {
      dispatch({
        type: `reduxModel/change${currentAction.payload.key}`,
        payload: currentAction.payload.values,
      })
    },
  },
  reducers: {
    ['SET_REDUXMODEL_TESTA'](state, payload) {
      return {
        ...state,
        ...payload,
      }
    },
    ['SET_REDUXMODEL_TESTB'](state, payload) {
      return {
        ...state,
        ...payload,
      }
    },
  },
}

那么你只需要在组件中,调用的默认 Action 即可

class MyComponent extends React.Component {
  componentDidMount() {
    this.props.dispatch({
      type: 'reduxModel/setStore',
      payload: {
        key: 'testA',
        values: '666',
      },
    })
  }
}

hooks ?

hooks 的出现,让我们看到了处理复杂且重复逻辑的曙光,那么问题来了,在 hooks 中能不能用 rc-redux-model ,我想说 : “想啥呢,一个是 react 的特性,一个是 redux 的中间件, 冲突吗?”

// Usage with React Redux: Typing the useSelector hook & Typing the useDispatch hook
// https://redux.js.org/recipes/usage-with-typescript#usage-with-react-redux
import { useDispatch } from 'react-redux'

export function useFetchUserInfo() {
  const dispatch = useDispatch()
  return async (userId: string) => {
    // 这里我选择自己处理异步,异步请求完后,再把数据传到 reducer 中
    const res = await callAPI(userId)
    if (res.code === 200) {
      dispatch({
        type: 'userModel/setStore',
        payload: {
          key: 'userInfo',
          values: res.data,
        },
      })
    }
  }
}

强调说明

rc-redux-model 出发点在于解决我繁琐重复的工作,store 文件分散,state 类型和赋值错误的问题,为此,对于跟我一样的用户,提供了一个写状态管理较为[舒服]的书写方式,大部分情况下兼容原先项目~

  • 为了解决[store 文件分散],参考借鉴了 dva 写状态管理的方式,一个 model 中写所有的 action、state、reducers
  • 为了解决[繁琐重复的工作],提供默认的 action,用户不需要自己写修改 state 的 action,只需要调用默认提供的 [model.namespace/setStore] 即可,从而将一些重复的代码从 model 文件中剔除
  • 为了解决[state 类型和赋值错误],在每次修改 state 值时候,都会进行检测,如果不通过则报错提示

How to use

FAQ

可在现有的项目中兼容使用,具体使用方式,可参考完整例子

其他文章

@PDKSophia PDKSophia added the documentation Improvements or additions to documentation label Aug 22, 2020
@PDKSophia PDKSophia pinned this issue Aug 22, 2020
@PDKSophia PDKSophia added docs 文档 and removed documentation Improvements or additions to documentation labels Aug 22, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs 文档
Projects
None yet
Development

No branches or pull requests

1 participant