RxJS 在项目中单独使用
单独使用RxJS提供的Subject来使用功能
一个组件的construtor中, 我们声明了如下的Subject
this.inputSubject = new Rx.Subject();
// 声明从收到数据,到数据传递给最终的数据订阅者,之间需要经过的数据处理管道
inputSubject
.debounceTime(1000)
.filter(value => value !== 'aa')
.do(value => setTitle(`搜索${value}中...`))
.concatMap(value => Rx.Observable.fromPromise(asyncQuery(value)))
.do(result => setTitle(`搜索${result.value}成功`))
.subscribe(updateList);
之后再在任何地方,我们拥有了对应的数据,需要把这个数据经过我们定义好的数据处理管道之后调用最终的updateList, 只需要如下的调用
<TextInput onChangeText={value => this.inputSubject.next(value)} />
这样的涉及各种异步和时间处理的写法不仅更加优雅, 而且后续进行修改来实现其他的功能也更加方便
Redux-Observable
Redux-Observable是配合RxJS使用的一个框架,用来在已有的redux框架下面添加一个中间件,把redux中的dispatch出来的action封装成一个流对象,之后使用RxJS的各种操作符对这个action进行各种操作。
安装过程
npm install redux-observable --save
npm install rxjs --save
Epic
Redux-Observable基本的操作单元是Epic,其本质是如下的一个函数
(action$, store) => (
action$.ofType('ACTION_TO_BE_LISTEN')
// ... All the processes and operators
.map(e => ({
type: 'NEW_DISPATCH_ACTION',
})) // finally dispatch a Observable back to the middleware
)
action$为传入的流对象,传入之后需要使用ofType
和filter
之类的过滤操作符筛选出来这个Epic需要监听的dispatch,之后对这个流时间各种RxJS里面定义的操作,最后生成一个新的Observable对象返回给redux中间件做后续的处理(调用其他action或者通过reducer更新store)
epic声明时候,使用redux-observable提供的combineEpics
方法来把声明的各种Epic合成到一起
const epic1 = action.ofType('ACTION_A').mapTo({ type: 'action_a' })
const epic2 = action.ofType('ACTION_B').mapTo({ type: 'action_b' })
const epic3 = action.ofType('ACTION_C').mapTo({ type: 'action_c' })
// ... So many epic here
import { combineEpics } from 'redux-observable';
const combinedEpics = combineEpics(epic1, epic2, ....)
和Reducer的声明非常类似。声明并生成了一个合并后的Epic,之后把这个Epic使用redux-observable提供的方法生成对应的中间件,在我们的RNX项目里面方法如下:
import { createEpicMiddleware } from 'redux-observable';
import combinedEpics from '../epics';
// RNX 配置项目, 本人自己使用的React框架是RNX
// 对于其他的Redux框架
// 在配置middleware的地方做类似操作即可
RNPlus.defaults.redux = {
// ... other configs like configing the reducers
middleware: [createEpicMiddleware(combinedEpics)],
}
参考项目blibee-demo里面的声明和使用epic的方法,可以看出这个框架可以很好的和我们现在的redux体系兼容
项目结构
some-action
|-index.js // 之前各种actions放置的地方
|-epic.js // 这个action里面需要使用epic来进行流控制的逻辑
|-query.js // 各种网络请求的, 都封装成一个简单的返回Promise的函数
其只是在现有redux基础上做功能的增量,任何epic没有涉及到的action都不会受到任何影响,保证了引入框架时候的兼容性。
开始介绍几个实际的demo
Redux-Observable使用中需要注意的问题
epic不能把传入的action流对象原样返回回去,这样会造成无限循环导致栈溢出。
// 一个type为SOME_ACTION_NAME的action不做处理就返回,会导致无限递归 action => ( action.ofType('SOME_ACTION_NAME') ) // 这样的只对传入的Observable进行时间维度的操作, // 仍然是把原先的action未做处理返回,仍然会导致无限递归 action => ( action.ofType('SOME_ACTION_NAME') .delay(1000) .debounceTime(3000) )
外界dispatch对应的action的时候,在redux框架里面,这个action是先派发到reducer里面进行处理,reducer中处理并更新过store之后,才到相同名称的epic中
// in reducer import { handleActions } from 'redux-actions'; export default handleActions({ SOME_EPIC_ACTION_SHARE_NAME(store, action) { return { ...store, count: store.count + 1 }; }, }, { count: 0 }); ... // in epic (action, store) => action.ofType('SOME_EPIC_ACTION_SHARE_NAME') .do(() => console.log(store.getState()) ... // in view dispatch(createAction('SOME_EPIC_ACTION_SHARE_NAME')); ... // 对应的输出结果 // { count: 1 }
监听同一个action的多个epic之间没有固定的执行顺序,全部按照epic流执行情况处理
case1:
// in epics action => action.ofType('SOME_ACTION') .do(() => console.log('epic 1')); action => action.ofType('SOME_ACTION') .do(() => console.log('epic 2')); // in view dispatch(createAction('SOME_ACTION')) // result will be // epic1 // epic2
case2:
// in epics action => action.ofType('SOME_ACTION') .delay(2000) .do(() => console.log('epic 1')); action => action.ofType('SOME_ACTION') .do(() => console.log('epic 2')); // in view dispatch(createAction('SOME_ACTION')) // result will be // epic2 // epic1 (after two second)
使用redux-observable来实现一些常规写法十分难以开发的场景。
- 异步轮询请求
- 按钮的delay/delayTime, buffer
- 异步请求的同步化/序列化
- 同步多个间隔不一样的轮询器