现在有很多关于redux镇上最新的孩子redux-saga / redux-saga的讨论。它使用生成器功能来侦听/调度动作。
在开始思考之前,我想知道使用优缺点的知识,redux-saga
而不是下面使用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 });
}
}
首先要注意的是,我们正在使用form调用api函数yield call(func, ...args)
。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 })
)
注意,我们通过简单地将模拟数据注入next
迭代器的方法来模拟api调用结果。模拟数据比模拟函数更简单。
注意的第二件事是对的调用yield take(ACTION)
。动作创建者会在每个新动作(例如LOGIN_REQUEST
)上调用Thunk
。即动作不断地 被推向 重击,重击无法控制何时停止处理这些动作。
在redux-saga中,生成器 拉出
下一个动作。也就是说,他们可以控制何时监听某个动作,何时不监听。在上面的示例中,流指令放置在while(true)
循环中,因此它将监听每个传入的动作,这在某种程度上模仿了thunk
push行为。
拉方法允许实现复杂的控制流程。例如,假设我们要添加以下要求
处理注销用户操作
首次成功登录后,服务器将返回一个令牌,该令牌会以一定的延迟过期,该令牌存储在expires_in
字段中。我们必须每expires_in
毫秒在后台刷新一次授权
考虑到在等待api调用的结果(初始登录或刷新)时,用户可能会在两次登录之间注销。
您将如何以笨拙的方式实现这一目标?同时还提供整个流程的完整测试覆盖范围?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)
赢得比赛(即用户单击注销按钮)。比赛将自动取消authAndRefreshTokenOnExpiry
后台任务。并且如果在通话过程authAndRefreshTokenOnExpiry
中被阻止,则该call(authorize, {token})
通话也会被取消。取消会自动向下传播。
您可以找到上述流程的可运行演示
现在有很多关于redux镇最新的孩子的讨论,Redux-Saga/Redux-Saga。它使用生成器函数来监听/调度操作。 在仔细考虑它之前,我想知道使用而不是下面使用和Async/await的方法的利弊。 组件可能如下所示,像往常一样分派操作。 然后我的行为如下所示:
Redux Thunk和Redux Saga都是Redux的中间件。两者之间的区别是什么?如何确定何时使用Redux Thunk或Redux Saga?
本文向大家介绍redux-saga和redux-thunk有什么本质的区别?相关面试题,主要包含被问及redux-saga和redux-thunk有什么本质的区别?时的应答技巧和注意事项,需要的朋友参考一下 saga 自己基本上完全弄了一套 asyc 的事件监听机制。虽然好的一方面是将来可以扩展成 worker 相关的模块,甚至可以做到 multiple threads 同时执行,但代码量大大增加
我有一个redux saga设置,工作正常。我的一个分派任务是创建一个新订单,然后一旦创建了订单,我就想用更新后的状态做一些事情。 由于 createOrder 操作触发调用 API 的重订传奇,因此存在延迟,因此在我的函数 do 之前不会更新此 .props.user 命令某些内容被调用。我可以设置一个超时,但这似乎不是一个可持续的想法。 我已经阅读了有关Stack Overflow的类似问题,
问题内容: 以下两种方法有什么区别? 什么时候需要使用fork,什么时候不需要? 问题答案: 通常,当传奇需要启动非阻塞任务时很有用。这里的非阻塞意味着:调用方启动任务并继续执行,而无需等待任务完成。 在许多情况下这可能有用,但主要有2种情况: 按逻辑域对Sagas进行分组 保留对任务的引用,以便能够取消/加入该任务 您的顶级传奇故事可能是第一个用例的示例。您可能会遇到类似: 其中可能包括了诸如:
我不明白需要这样的东西。根据我的理解,是一个返回函数的函数。在我看来,包装的表达式和中间件的使用更容易混淆正在发生的事情。摘自的示例代码 上面的代码可以写得更简洁和直观: 我的问题是,实现了什么需求,以及它如何改进类似于上面示例的现有解决方案。