现在有很多人在谈论redux城的最新成员,redux-saga/redux-saga。它使用生成器函数来监听/分派动作。
在我把我的脑袋绕在它周围之前,我想知道使用redux-saga的优点/缺点,而不是下面的方法,我使用redux-thunk与async/await。
组件可能是这样的,像往常一样分派动作。
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- thunk和Redux- saga在一些重要的方面有所不同,它们都是Redux的中间件库(Redux中间件是通过dispatch()方法拦截进入商店的操作的代码)。
一个动作可以是任何东西,但是如果您遵循最佳实践,那么一个动作就是一个简单的javascript对象,其中包含一个类型字段,以及可选的有效负载、元和错误字段。如。
const loginRequest = {
type: 'LOGIN_REQUEST',
payload: {
name: 'admin',
password: '123',
}, };
Redux-Thunk
除了分派标准的动作,redux -坦克中间件还允许你分派特殊的函数,称为坦克。
坦克(在Redux中)通常有以下结构:
export const thunkName =
parameters =>
(dispatch, getState) => {
// Your application logic goes here
};
也就是说,一个thunk是一个(可选的)接受一些参数并返回另一个函数的函数。内部函数接受一个分派函数和一个getState函数——这两个函数都将由Redux-Thunk中间件提供。
Redux-Saga
Redux-Saga中间件允许您将复杂的应用程序逻辑表示为称为saga的纯函数。从测试的角度来看,纯函数是可取的,因为它们是可预测和可重复的,这使得它们相对容易测试。
saga是通过称为生成器函数的特殊函数实现的。这些是ES6 JavaScript的新特性。基本上,在任何看到yield语句的地方,执行都会在生成器中跳跃。将yield语句看作是导致生成器暂停并返回yield值的语句。稍后,调用者可以在yield之后的语句处恢复生成器。
生成器函数是这样定义的。注意function关键字后面的星号。
function* mySaga() {
// ...
}
一旦登录传奇注册到Redux-Saga。但是在第一行上的yield take将暂停saga,直到带有“LOGIN_REQUEST”类型的操作被分派到商店。一旦发生这种情况,执行将继续进行。
欲了解更多细节,请参阅本文。
为了给这个答案一些上下文:嗨,我是Redux维护者。
我们最近在Redux文档中添加了一个新的Side Effects methods页面,它将提供关于所有这些内容的许多信息,但我将尝试在这里写一些简短的内容,因为这个问题得到了大量的曝光。
在2022年,我们将监听器中间件添加到官方Redux工具包中,用于“响应式Redux逻辑”。它可以做sagas可以做的大部分事情(例外是通道),而不需要生成器语法和更好的TypeScript支持。
但这并不意味着你应该用侦听器中间件来编写所有的东西——我们建议在可能的情况下总是先使用thks,在thks不能做你想做的事情时再使用侦听器中间件。
一般来说,我们在2023年的立场是,只有当你有其他中间件无法满足的特定需求时,你才应该使用saga。(本质上:如果你需要频道。)
我们的建议是:
数据抓取
使用RTK Query作为数据获取和缓存的默认方法
如果RTKQ不完全适合,使用createAsyncThunk
只有在其他方法都没用的情况下,才会求助于手写的感谢信
不要使用saga或可观察对象来获取数据!
对动作/状态变化做出反应,异步工作流
使用RTK监听器作为默认响应存储更新和编写长时间运行的异步工作流
只有当监听器不能很好地解决你的用例时,才使用saga / observable
带有状态访问的逻辑
对于复杂的同步和适度的异步逻辑,包括对getState的访问和调度多个操作,可以使用坦克
我只是想从我的个人经验(使用sagas和thunk)中补充一些评论:
传奇故事很适合测试:
您不需要模拟用效果包装的函数
因此,测试是干净的,可读的,易于编写的
在使用saga时,动作创建者通常会返回简单的对象字面量。与thunk的承诺不同,它也更容易测试和断言。
传说更有力量。你在一个坦克动作创造者中可以做到的所有事情,你也可以在一个传奇中做到,但反之亦然(至少不容易)。例如:
等待一个/多个动作被分派(采取)
取消现有的例程(取消,taklatest, race)
多个例程可以监听相同的动作(take, takeEvery,…)
Sagas还提供了其他有用的功能,它概括了一些常见的应用程序模式:
监听外部事件源的通道(例如websockets)
Fork模型(Fork, spawn)
节气门
...
Sagas are great and powerful tool. However with the power comes responsibility. When your application grows you can get easily lost by figuring out who is waiting for the action to be dispatched, or what everything happens when some action is being dispatched. On the other hand thunk is simpler and easier to reason about. Choosing one or another depends on many aspects like type and size of the project, what types of side effect your project must handle or dev team preference. In any case just keep your application simple and predictable.