Saga的来源
我们都知道,技术都是为了解决某个或某类问题而产生的。
而Saga
作为Redux
的功能增强,所以,我们先来看看单独使用Redux
会遇到什么问题。
1、Redux基础知识回顾
Redux中三个核心概念:Action
、Reducer
、Store
,Redux专门用来管理状态。
这句话貌似很难理解,没有使用Redux
之前我们会怎么做?
没有使用Redux
之前,我们会直接对数据进行操作与修改,所有的功能操作都在一个地方进行处理,当应用越来越复杂的时候,应用状态会变得非常非常乱。
就比如说,一个饭店想要的是新鲜的猪肉,饭店需要自己去养猪,把猪养大之后还需要屠宰,清洗等等...什么事都自己干。
除了新鲜的猪肉,饭店还要新鲜的青菜,它还需要去种菜,摘菜...另外,饭店可能还需要海鲜、辅料...等等食材。
忙不过来是一回事,会增大出错的成本:比如摘来的菜不小心喂猪去了。
所以就有了Redux
:
Redux有三个核心的概念——Store
、Action
、Reducer
。
就像人的器官一样,各种细胞进行了分化,专门的器官负责专门的事情,高效且不容易出错。
在Redux中,Store
dispatch 一个 Action
,交予Reducer
进行处理,Reducer
处理完把结果返回更新Store
。
就像是这个饭店,它发出了一个Action
说:“我要新鲜的猪肉和青菜!”。随即Reducer
接收到这个Action
之后进行处理,最终给饭店带来的新鲜的猪肉与青菜,具体过程在Reducer
中进行处理,饭店不用过问。
这样一来,Redux
就帮我们解决了一个状态混乱的问题。?
2、Saga来了
那单独用Redux
还是不够的! Reducer
需要处理的事情就太多了:主要业务逻辑、sideEffects(副作用, 例如异步获取数据,访问浏览器缓存等。)
在这个例子中,Reducer
中除了养猪还要屠宰,而屠宰还需要在养猪之后进行。同样的,Reducer
除了种菜还要摘菜,但是摘菜需要菜长大了才能摘。这样一来,Reducer
会变得很臃肿,里面也会乱成一团麻。
这个时候,它就需要帮手来帮忙了。
我们需要对Reducer
的功能进行进一步的分工处理,这时候,Saga
出场了。
Saga负责养猪、种菜...把一些异步处理,读取缓存区数据的操作交给Saga去做,在技术上叫做“sideEffects
”(副作用):
那么Reducer
怎么知道猪养好了,菜长大了呢?天天守在那里吗?
不需要!Saga
负责养猪还有种菜之外,等到猪长大和菜可以摘了,就告知Reducer
一声:来屠宰了!来摘菜了!Reducer
接收到信息后进行相关的操作。
也不需要Reducer
天天守在那里看猪长大没,菜可以吃了没...(会阻塞进程的。)
那么Saga
如何通知Reducer
呢?
我们知道,Reducer
接收的是state还有action两个对象作为参数,然后通过计算返回一个心的state,对于其他格式的“通知信息”是没办法识别。因此,Saga
通知Reducer
的时候,肯定也是dispatch一个Action
交予Reducer
进行处理。
这样一来,很多语法规则的由来都是有章可循的。不知道你看懂了没?
Saga的基础知识
**Saga
是一个 Redux中间件,意味着这个线程可以通过正常的Redux Action
从主应用程序启动,暂停和取消,它能访问完整的Redux state
,也可以dispatch Redux Action
。
一个 Saga
就像是应用程序中一个单独的线程,它独自负责处理副作用。**
中间件
中间件就是非业务的技术类组件。它介于底层逻辑与业务之间,相当于中介的作用。
如果应用了中间件,程序会先经过中间件的处理之后再返回给具体业务进行处理。我们可以把应用公用部分,或者是需要进行异步操作的部分在中间件中进行处理,例如用户登录、数据请求等。
在Redux中,当我们dispatch 一个Redux Action的时候,Saga作为处理的中间件,它可以接收到Action之后进行相关的处理,并把相关的处理交给对应的Reducer再进行处理。
Saga核心API
1、辅助函数
takeEvery
例如:每次点击 “1秒后加1” 按钮时,我们发起一个 incrementAsync
的 action。
首先我们创建一个将执行异步 action 的任务:
import { delay, put } from 'redux-saga/effects'
function* incrementAsync() {
// 延迟1s
yield delay(1000)
yield put({
type: 'increment'
})
}
然后在每次 incrementAsync action
被发起时启动上面的任务.
import { takeEvery } from 'redux-saga'
function* watchIncrementAsync() {
yield takeEvery('incrementAsync', incrementAsync)
}
takeLatest
在上面的例子中,takeEvery
是每次发起“incrementAsync” action的时候都会执行。如果我们只想得到最新那个请求的响应,我们可以使用 takeLatest
辅助函数
import { takeLatest } from 'redux-saga'
function* watchIncrementAsync() {
yield takeLatest('incrementAsync', incrementAsync)
}
2、Effect Creators
redux-saga
框架提供了很多创建effect的函数,下面是开发中最常用的几种:
- take(pattern)
- put(action)
- fork(fn, ...args)
take(pattern)take
函数可以理解为监听未来的action,它创建了一个命令对象,告诉middleware
等待一个特定的action
,直到一个与pattern匹配的action
被发起,才会继续执行下面的语句。
put(action)put
函数是用来发送action的 effect,你可以简单的把它理解成为Redux框架中的dispatch
函数,当put
一个action后,reducer中就会计算新的state并返回。
function* incrementAsync() {
// 延迟1s
yield delay(1000)
yield put({
type: 'increment'
})
}
fork(fn, ...args)fork
函数是用来调用其他函数的,但是fork函数是非阻塞函数,也就是说,程序执行完 yield fork(fn, args)
这一行代码后,会立即接着执行下一行代码语句。
Redux-Saga语法
讲完了Saga
的语法,我们来看看Redux与Saga配合使用的语法知识:
1、创建一个最简单的Saga
// saga.js
function* helloSaga() {
console.log('Hello saga!')
}
export default helloSaga
2、把Saga
作为中间件使用
这里使用的是createSagaMiddleware
函数,创建一个saga中间件:
import createSagaMiddleware from 'redux-saga'
const sagaMiddleware = createSagaMiddleware()
3、Redux
如何添加中间件
这里使用的是applyMiddleware
函数,应用saga中间件:
import { createStore, applyMiddleware } from 'redux'
const store = createStore(reducer,
applyMiddleware(sagaMiddleware)
)
4、运行
import helloSaga from './saga'
sagaMiddleware.run(helloSaga)
完整?代码示例
myComponent.js
import React from 'react'
import ReactDOM from 'react-dom'
class MyComponent extends React.Component {
render() {
return (
<div className="index">
<p>{this.props.count}</p>
<button onClick={this.props.increment}>加1</button>
<button onClick={this.props.incrementAsync}>1秒后加1</button>
</div>
)
}
}
export default MyComponent
reducer.js
export default function reducer(state = {
count: 10
}, action) {
switch (action.type) {
case 'increment': {
return {
count: state.count + 1
}
}
default:
return state
}
}
App.js
import { connect } from 'react-redux'
import MyComponent from './myComponent'
// Map Redux state to component props
function mapStateToProps(state) {
console.log('state', state)
return {
count: state.count
}
}
// Map Redux actions to component props
function mapDispatchToProps(dispatch) {
return {
increment: () => dispatch({
type: 'increment'
}),
incrementAsync: () => dispatch({
type: 'incrementAsync'
})
}
}
// Connected Component
const App = connect(
mapStateToProps,
mapDispatchToProps
)(MyComponent)
export default App
main.jsx
import "regenerator-runtime/runtime"
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import createSagaMiddleware from 'redux-saga'
import rootSaga from './saga'
import App from './App'
import reducer from './reducer'
const sagaMiddleware = createSagaMiddleware()
// Store
const store = createStore(reducer,
applyMiddleware(sagaMiddleware)
)
sagaMiddleware.run(rootSaga)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.body.appendChild(document.createElement('div'))
)
saga.js
import { delay, put, takeEvery, all } from 'redux-saga/effects'
function* helloSaga() {
console.log('Hello saga!')
}
function* incrementAsync() {
// 延迟1s
yield delay(1000)
yield put({
type: 'increment'
})
}
function* watchIncrementAsync() {
yield takeEvery('incrementAsync', incrementAsync)
}
export default function* rootSaga() {
yield all([
helloSaga(),
watchIncrementAsync()
])
}
Github源码参考:https://github.com/kexinWeb/r...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。