react-redux 学习之redux-saga

首先,这个redux-saga和redux-thunk做的是同样的事情, 就是解决我们在redux中做异步的事情. 但 redux-saga 所涉及到的知识点和代码的复杂程度上都要复杂一些.

redux-saga 是基于ES6 的 Generator来实现的异步请求数据.

这是一个Generator例如:

1
2
3
4
function* g() {
var res = yield 1 + 2
return res
}

以上就是一个用同步方式来实现异步请求的Generator 函数. 更多的需要参考其文档. 这里就吧赘述了,因为我们的目的是记录学习 Redux-sagareact中的应用.

那么,我们如何使用Redux-saga来实现异步数据的交互呢? 这里我们列举一个完整的使用Reudx-saga来构建我们之前TodoList中数据获取部分的代码

首先,我们需要创建一个saga目录,用于存储我们的saga generator函数,具体代码内容贴如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { takeLatest, put, call, fork, all } from 'redux-saga/effects'
import * as Api from '../api'
import * as actions from '../store/action'

/**
* get all todo list
*/
export function* getTodoList () {
yield put({type: 'SHOW_LOADING'})
try{
const todos = yield call(Api.fetchTodoList)
yield put(actions.setTodos(todos.data.todoList))
} catch (e) {
console.log(e)
}
yield put({type: 'HIDE_LOADING'})
}

/**
* watch getTodoList state
*/
export function* watchGetTodoList () {
yield takeLatest(actions.GET_TODOS, getTodoList)
}

export default function* rootSaga () {
yield all([fork(watchGetTodoList)])
}

注意, 我们这里所有的函数都是Generator函数, 并且我们使用了saga的内置辅助函数来协助我们实现异步.

我们可以注意到以上的getTodoList()函数,首先我们需要触发SHOW_LOADINGaction来让列表显示一个加载状态, 然后我们使用了辅助函数call来调用执行我们的API接口函数,这个函数就是一个我们普通的
会返回一个Promise对象的HTTP函数.

我们看下api接口函数的定义:

1
2
3
4
5
import * as http from '../utils/http'

export async function fetchTodoList () {
return http.get('/getTodos')
}

非常简单, 很普通,这里不做过多说明了…

接着, 我们通过yield这个ES6的表达式让异步获取数据结束后,我们通过辅助函数put来执行下一个action,这里的put就好像redux本身的dispatch一样,是执行action的方法.
这样,我们就可以通过执行setTodo()这个action来执行其对应的reducer,继而更新state状态.
看到没, 我们并没有修改任何station, 也没有修改reducer. 我们这是在这里通过Generator函数异步执行了对应的action而已.

那么,继而有个问题出现, 就是saga怎么知道什么时候来执行这个Generator方法,然后触发更新呢?

接着,我们看刚才的第二段代码, 即 watchGetTodoList()函数. 这个里面用到了一个takeLatest辅助函数. 它的作用就是,不论你请求了多少次对应的action,我只取最后一次的数据. (注意,请求还是多次,并不会有debounce效果,谁让它不是RxJS呢 - -!).
OK,看代码, saga会监听我们的actions.GET_TODOS这个action,每当监听到我们执行了这个action,就会立即执行其第二个参数里的函数(必须是Generator).即我们代码里的getTodoList .

紧接着, 我们导出并执行我么的监控函数, 这里使用了fork辅助函数, 其和call使用的目的都是一样,区别就是fork是非阻塞的,call是阻塞的. 点这里看官方中文说明文档;

最后, 我们来修改一下index.js.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import React from 'react';
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddelware from 'redux-saga'
import reducers from './store/reducer'
import rootSaga from './saga'
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

import 'element-theme-default'

const sagaMiddleware = createSagaMiddelware()

/**
* Create Redux Store
*/
const store = createStore(
reducers,
// binding redux devtool via chrome
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
//
applyMiddleware(sagaMiddleware)
)

sagaMiddleware.run(rootSaga)

render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
registerServiceWorker();

以上是将前面咱们学的redux-thunk干掉了,换成了redux-saga .


我这里贴下action 是怎么写的. 其实是最原始的action,并没有任何特殊的地方, 我觉得就这点是让我最舒服的地方.我喜欢无侵入的设计

Action

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
export const ADD_TODO = 'ADD_TODO'
export const GET_TODOS = 'GET_TODOS'
export const SET_TODOS = 'SET_TODOS'
export const TOGGLE_TODO = 'TOGGLE_TODO'
export const REMOVE_TODO = 'REMOVE_TODO'
export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER'

let todoListId = 0
export const addTodo = text => ({
type: ADD_TODO,
id: todoListId++,
text
})
export const getTodos = () => ({
type: GET_TODOS
})

export const setTodos = (todos) => ({
type: SET_TODOS,
todos
})

export const toggleTodo = id => ({
type: TOGGLE_TODO,
id
})

export const removeTodo = id => ({
type: REMOVE_TODO,
id
})

export const setVisibilityFilter = filter => ({
type: SET_VISIBILITY_FILTER,
filter
})

export const VisibilityFilters = {
SHOW_ALL: 'SHOW_ALL',
SHOW_COMPLEATED: 'SHOW_COMPLEATED',
SHOW_ACTIVE: 'SHOW_ACTIVE'
}

Redux-saga 的优势(自我感觉)

  • 代码结构可以完全清晰的结构
  • 不侵入现有的reduxaction
  • 构建在Generator之上,让异步函数写起来和同步函数别无二致

Redux-saga 的不足(XJB扯)

  • 所需要的知识点过于繁杂,
  • 虽然代码结构可以清晰,逻辑代码(非业务逻辑)不管会出现在Reducer中, 也会出现在saga里.

另外,想说的是, 对于redux-saga来说, 目前学习的仍然是皮毛,后续还可以再研究研究 :P.