是时候想想redux、mobx、react Context、GraphQL

是时候想想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里取。

部分代码:

// action.js
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。

感谢阅读。

文章作者: 韦宗圻
文章链接: https://www.weizongqi.com/2018/04/20/Context、GraphQL/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 wiki
支付宝打赏
微信打赏