javascript – 使用带ES6发生器的redux-saga与使用ES2017 async / await的redux-thunk的优点/缺点
作者:互联网
现在有很多关于redux镇最新小孩的讨论,redux-saga/redux-saga.它使用生成器功能来收听/调度动作.
在我绕过它之前,我想知道使用redux-saga而不是下面的方法的优点/缺点,我在使用带有async / await的redux-thunk.
组件可能看起来像这样,像往常一样调度动作.
import { login } from 'redux/auth';
class LoginForm extends Component {
onClick(e) {
e.preventDefault();
const { user, pass } = this.refs;
this.props.dispatch(login(user.value, pass.value));
}
render() {
return (<div>
<input type="text" ref="user" />
<input type="password" ref="pass" />
<button onClick={::this.onClick}>Sign In</button>
</div>);
}
}
export default connect((state) => ({}))(LoginForm);
然后我的行为看起来像这样:
// auth.js
import request from 'axios';
import { loadUserData } from './user';
// define constants
// define initial state
// export default reducer
export const login = (user, pass) => async (dispatch) => {
try {
dispatch({ type: LOGIN_REQUEST });
let { data } = await request.post('/login', { user, pass });
await dispatch(loadUserData(data.uid));
dispatch({ type: LOGIN_SUCCESS, data });
} catch(error) {
dispatch({ type: LOGIN_ERROR, error });
}
}
// more actions...
// user.js
import request from 'axios';
// define constants
// define initial state
// export default reducer
export const loadUserData = (uid) => async (dispatch) => {
try {
dispatch({ type: USERDATA_REQUEST });
let { data } = await request.get(`/users/${uid}`);
dispatch({ type: USERDATA_SUCCESS, data });
} catch(error) {
dispatch({ type: USERDATA_ERROR, error });
}
}
// more actions...
解决方法:
在redux-saga中,相当于上面的例子
export function* loginSaga() {
while(true) {
const { user, pass } = yield take(LOGIN_REQUEST)
try {
let { data } = yield call(request.post, '/login', { user, pass });
yield fork(loadUserData, data.uid);
yield put({ type: LOGIN_SUCCESS, data });
} catch(error) {
yield put({ type: LOGIN_ERROR, error });
}
}
}
export function* loadUserData(uid) {
try {
yield put({ type: USERDATA_REQUEST });
let { data } = yield call(request.get, `/users/${uid}`);
yield put({ type: USERDATA_SUCCESS, data });
} catch(error) {
yield put({ type: USERDATA_ERROR, error });
}
}
首先要注意的是我们使用表单yield调用(func,… args)来调用api函数. call不执行效果,它只是创建一个像{type:’CALL’,func,args}这样的普通对象.执行被委托给redux-saga中间件,后者负责执行该函数并使用其结果恢复生成器.
主要优点是您可以使用简单的相等性检查在Redux之外测试生成器
const iterator = loginSaga()
assert.deepEqual(iterator.next().value, take(LOGIN_REQUEST))
// resume the generator with some dummy action
const mockAction = {user: '...', pass: '...'}
assert.deepEqual(
iterator.next(mockAction).value,
call(request.post, '/login', mockAction)
)
// simulate an error result
const mockError = 'invalid user/password'
assert.deepEqual(
iterator.throw(mockError).value,
put({ type: LOGIN_ERROR, error: mockError })
)
注意我们只是通过将模拟数据注入迭代器的下一个方法来模拟api调用结果.模拟数据比模拟函数更简单.
要注意的第二件事是对收益率的要求(行动).动作创建者在每个新动作(例如LOGIN_REQUEST)上调用Thunks.即动作不断被推到雷鸣声中,并且无法控制何时停止处理这些动作.
在redux-saga中,发电机开始下一步行动.即他们有权控制何时采取某些行动,何时不采取行动.在上面的示例中,流指令被放置在while(true)循环内,因此它将侦听每个传入的操作,这有点模仿thunk推送行为.
拉方法允许实现复杂的控制流程.例如,假设我们要添加以下要求
>处理LOGOUT用户操作
>在第一次成功登录时,服务器返回一个令牌,该令牌在存储在expires_in字段中的某些延迟中到期.我们必须在每个expires_in毫秒的后台刷新授权
>考虑到在等待api调用的结果(初始登录或刷新)时,用户可以在中间注销.
你如何用thunk实现它;同时还为整个流程提供全面的测试覆盖?以下是Sagas的外观:
function* authorize(credentials) {
const token = yield call(api.authorize, credentials)
yield put( login.success(token) )
return token
}
function* authAndRefreshTokenOnExpiry(name, password) {
let token = yield call(authorize, {name, password})
while(true) {
yield call(delay, token.expires_in)
token = yield call(authorize, {token})
}
}
function* watchAuth() {
while(true) {
try {
const {name, password} = yield take(LOGIN_REQUEST)
yield race([
take(LOGOUT),
call(authAndRefreshTokenOnExpiry, name, password)
])
// user logged out, next while iteration will wait for the
// next LOGIN_REQUEST action
} catch(error) {
yield put( login.error(error) )
}
}
}
在上面的例子中,我们使用race来表达我们的并发性要求.如果take(LOGOUT)赢得比赛(即用户点击了Logout按钮).比赛将自动取消authAndRefreshTokenOnExpiry后台任务.如果authAndRefreshTokenOnExpiry在呼叫中间被阻止(authorize,{token}),则它也将被取消.取消自动向下传播.
你可以找到一个runnable demo of the above flow
标签:redux-saga,javascript,reactjs,redux,redux-thunk 来源: https://codeday.me/bug/20190919/1812584.html