是时候想想redux、mobx、react
在16年的时候,React行业内就开始提出,我们可能不需要Redux。渐渐的,这个声音持续扩大,甚至前几天React Amsterdam 2018在讲GraphQL的时候就提出Redux的缺点。那么当这些声音慢慢的在我的身边围绕的时候,我是时候想想Redux、Mobx、React Context、GraphQL了。
Redux
我在2016年的时候,开始使用Redux。我遵循React的官网的提示,大致意思是:我们不建议你使用context来做子组件的数据传输,我们希望你使用flux、Redux做数据管理。当然,我按照这个建议去做了。
结果,我在使用Redux的时候,我发现我要创建一个数据,我就要创建action、reducer、constant。当我在使用异步接口的时候,我会有一些懵逼。因为Redux本身做的事情很简单,state -> action -> reducer -> state。然后这一过程是恒定,没有副作用的,这个观点我很赞成。
但是让我崩溃的是,我要请求后端拿数据的时候,产生的副作用,在Redux中是反模式的,所以Redux提供的一个异步操作的中间件redux-thunk。接着在页面上要取state的数据的时候,还要根据所需要的数据去rootState里取。
部分代码:
import { get } from './service' import AsyncAction from './AsyncAction'
const asyncAction = new AsyncAction('GET', 'ORDER') export const { SELECT_QUERY, INVALIDATE_CHANGE, ASNYC_DATA, ASNYC_SUCCESS, ASYNC_ERROR } = asyncAction.getActionName()
const selectQuery = asyncAction.selectQuery const invalidateChange = asyncAction.invalidateChange const requestPosts = asyncAction.requestPosts const asyncSuccess = asyncAction.asyncSuccess const asyncError = asyncAction.asyncError const shouldFetchPosts = asyncAction.shouldFetchPosts
export const getOrder = query => ((dispatch, getState) => { dispatch(selectQuery(query)) dispatch(invalidateChange()) const $$state = getState() const $$order = $$state.get('order') if (shouldFetchPosts($$order)) { return dispatch(fetchPosts()) } })
const fetchPosts = () => ( async (dispatch, getState) => { const $$state = getState() const $$order = $$state.get('order') const $$query = $$order.get('query')
dispatch(requestPosts()) const res = await get(`/api/order/${$$query.get('orderId')}`) if (parseInt(res.error_code, 10) === 0) { dispatch(asyncSuccess({ errorCode: res.error_code, errorMsg: res.error_msg, payload: { list: res.data } })) } else { dispatch(asyncError({ errorCode: res.error_code, errorMsg: res.error_msg })) } return res } )
// reducer.js import { fromJS } from 'immutable' import { SELECT_QUERY, INVALIDATE_CHANGE, ASNYC_DATA, ASNYC_SUCCESS, ASYNC_ERROR } from './order.action' const initialState = fromJS({ query: {}, payload: { list: [] }, errorCode: 0, errorMsg: '', isFetching: false, invalidate: false, lastUpdated: '' })
let switchMap = {}
switchMap[SELECT_QUERY] = (state, action) => state.set('query', fromJS(action.query)) switchMap[INVALIDATE_CHANGE] = state => state.set('invalidate', true) switchMap[ASNYC_DATA] = state => state.merge(fromJS({ errorCode: 0, errorMsg: '', isFetching: true })) switchMap[ASNYC_SUCCESS] = (state, action) => state.merge(fromJS({ isFetching: false, invalidate: false, payload: action.payload, errorCode: action.errorCode, errorMsg: action.errorMsg, lastUpdated: action.receivedAt })) switchMap[ASYNC_ERROR] = (state, action) => state.merge(fromJS({ isFetching: false, invalidate: false, payload: { list: [] }, errorCode: action.errorCode, errorMsg: action.errorMsg, lastUpdated: action.receivedAt }))
export default (state = initialState, action) => { if (switchMap[action.type]) { return switchMap[action.type](state, action) } else{ return state } }
// OrderPage.js import Order from './Order' export default connect(state => reduxUtils.mapFetchStateToProps(state, 'order'))(Order)
|
这时候,市面上为了解决action、异步操作、取数据、表单问题就催生出来很多中间件:
- redux-actions
- redux-saga / redux-thunk
- reselect
- react-form
等等。
然后我们前端人员就要苦逼的去一遍遍看这些文档,了解中心思想。
所以对于Redux,我一点都喜欢不上来
Mobx
从Redux种种问题,我的目光转向的Mobx,一个响应式State管理。对于Mobx,可以说比Redux自由度更高,结构逻辑清晰。即可使用到全局Store中,然后再注入至页面,也可以单独在一个组件使用。
所以在箱信(之前所在的公司)要重构系统的时候,我把这个作为选型。在各方面性能、维护性、易用性、学习成本上都满足我们的业务需求。
上面Redux实现的Order,在Mobx之中很简单的实现:
import { observable, action, computed, runInAction } from 'mobx' import axios from 'modules/utils/axios' import BaseRequestModel from 'modules/actions/common/BaseRequestModel' import { createId } from 'modules/utils' import { getUrl } from 'modules/utils/url' import trim from 'lodash/trim'
const defaultQuery = { order_code: '', page: 1, status: 200 }
export default class Orders extends BaseRequestModel { @observable query = Object.assign({}, defaultQuery) list = observable([])
@action initQuery (query: Object) { const keys = Object.keys(query) keys.forEach((key) => { let value = query[key] if (key === 'page' || key === 'status') { value = parseInt(value, 10) } this.setQuery(key, value) }) if (keys.indexOf('status') === -1) { this.setQuery('status', 200) } }
@action setQuery (key: string, value: any) { if (key in this.query) { this.query[key] = value } }
@action restQuery () { this.query = Object.assign({}, defaultQuery) }
@action async getOrder () { this.updateTime = new Date('') const res = await axios.get('/api/order') if (res.error_code === 0) { runInAction('getOrdersSuccess', () => { this.list.replace(res.data) this.errorCode = res.error_code this.errorMsg = res.error_msg this.updateTime = new Date() }) } else { runInAction('getOrdersFailed', () => { this.list.replace([]) this.errorCode = res.error_code this.errorMsg = res.error_msg this.updateTime = new Date('') }) } } }
|
可能唯一的缺点就是不支持immutable吧,但已经有人在mobx的基础上将immutable结合,在github上一下就成为星星大户,Immer.js。
对于我目前的观点来说,我还是主推Mobx响应式编程。
React Context
在React16.3.0之前的版本,Context是一个极其难用的东西,作用域,生命周期等等都是被人所诟病的。写过基础组件的人都知道,Context确实如官方所说尽量不要使用。
从React16.3.0更新后,Context的用法有了质的变化,可以参考React Context文档,这里不细说。从React所提供的API来看,React Context是有可能取缔Redux的。新版Context的作用域很清晰,生命周期可以在父层组件中的state管理。
但如果要把Context用在大型前端上面,会有不小的坑要踩,目前还是以观望为主。
GraphQL
从React Amsterdam 2018会议上,GraphQL确实很吸引人。
但是它有一个缺点,就是需要后端人员配合,在原有的架构上去改这一套东西的话,周期、成本都是一个非常大的开销。如果是新启的项目,倒是可以尝试的去做。
有一点变化的是,AWS已经支持GraphQL的服务了。全球云服务商龙头AWS开始支持GraphQL之后,那么国内的阿里云、各种云等等也会在这一方面支持。到时候也许没有所谓的联调、接口等概念,后端更专注的去做数据清洗计算等等。这是一个趋势,也是前端进化的必然结果。
总结
我不喜欢Redux。我不喜欢Redux。我不喜欢Redux。我不喜欢Redux。我不喜欢Redux。
感谢阅读。