React-rxjs 给出一个新思路让 rxjs 更好与 react 结合
-
链接 View 与 Store 层,无感 rxjs
import { inject } from 'react-rxjs' import store$, { inc, dec } from './store' import MyComponent from './view' const props = (storeState: number): MyProps => { number: storeState, inc, dec } export default inject(store$, props)(MyComponent)
storeState
是 store 的全部数据(react-rxjs 是多 store 思想,所以 inject 第一个参数传入不同的 store,组件会与对应的 store 绑定)
-
store 层必须将 rxjs 与 action 对接
import { createStore } from 'react-rxjs' const inc$ = new Subject<void>() const des$ = new Subject<void>() const reducer$: Observable<(state: number) => number> = Observable.merge( inc$.map(() => (state:number) => state + 1) dec$.map(() => (state: number) => state - 1) ) const store$ = createStore("example", reducer$, 0) export inc = () => inc$.next() export dec = () => dec$.next() export default store$
const inc$ = new Subject<void>()
export inc = () => inc$.next()
-
Rxjs 两种代码组织方式
- 数据源的抽象、聚合
- 对已经聚合过的单一数据源订阅后进行处理,处理过程只包含对本数据源的操作,不再进行 merge 其他数据
-
rxjs 的优势:抽象(副作用隔离),以及强大的流处理能力
-
Action 部分没有足够的抽象能力就无法进行流的 merge,如果生成实例自己就是一个事件触发器,想要进行流合并,就必须在 reducer 中执行
const incReducer = inc$.merge(requestUser$).map(() => (state: number) => state + 1)
-
React-rxjs 的形式解决了 rxjs 与 react 结合繁琐的问题,如果按照规范,Action 的功能比较弱,无法进行下一步抽象
// 单一数据源下载dispatch过程触发事件进入action,每个action都源自同一数据源订阅,通过action.type筛选确保执行正确的action
const pingEpic = action$ =>
actions$.filter(action => action.type === 'PING')
.delay(1000) // Asychronously wait 1000ms then continue
.mapTo({type: 'PONG'})
// later...
dispatch({type: 'PING'})
// reducer中心化处理做过滤
const pingReducer = (state = { isPinging: false }, action) => {
switch(action.type) {
case 'PING':
return { isPinging: true };
case 'PONG':
return { isPinging: false };
default:
return state;
}
}
-
比上优势是:
第一部分是数据源的抽象、聚合,第二部分是对已经聚合过的单一数据源订阅后进行处理,这里处理过程只包含对本数据源的操作,不能 merge 其他数据源
-
比下缺点:
redux-observable 的样板代码只会比 react-redux 多,投入使用的话较好的方式是按照 dva 的思路,减少 redux-observable 的样板代码
-
最后可以围观一下 cyclejs,虽然此库脱离了 react 生态,但是他可以解决 react+rxjs 的循环依赖(A 依赖 B, B 依赖 A)的问题:视图的回调函数可以产生数据源
function main(sources) { const input$ = sources.DOM.select('.field').events('input') const name$ = input$.map(ev => ev.target.value).startWith('') const vdom$ = name$.map(name => div([ label('Name:') input('.field', {attrs: {type: 'text'}}) hr() h1('Hello' + name), ]) ) return { DOM: vdom$ } }
-
rxjs 还是难以落地到 react 业务代码中,本质上没有 cyclejs 这种机制解决数据源引起的循环依赖的问题