跳转到主文档

effects

补充信息

effects 用于封装各种副作用函数。

提示
  • @modern-js-reduck/plugin-effects 内置于 Modern.js,默认开启。
  • 可通过 modern.config.js 关闭插件。

actions 是用来同步修改 state 的(纯函数、无副作用)。但应用开发的过程中,会有大量的异步请求或者封装某些操作等各种副作用的调用,所以这里将各种副作用的函数(Effect Handler)封装在 effects 模块。

示例

Effect Handler 函数类型包括 3 种:

类型签名
type VoidEffect = (...args: any[]) => void;
type PromiseEffect = (...args: any[]) => Promise<any>;
type ThunkEffect = (...args: any[]) => (dispatch?, getState?) => void | Promise<any>;

interface ModelEffects {
[effectKey: string]: VoidEffect | PromiseEffect | ThunkEffect,
}

VoidEffect

副作用调用并不只是异步请求,比如还有 localStorage、DOM 操作等,这些操作没有返回,那么也无需进行 pending、fulfilled、rejected 等阶段的处理。

对于不需要进行 pending、fulfilled、rejected 等阶段处理的异步请求也可以选择不 return。

示例
import { model, useModel } from '@modern-js/runtime/model';

const fooModel = model('foo').define((_, { use }) => {
return {
state: {
value: 'foo'
},
effects: {
async load() {
const resp = await fetch('xxxx');
const [, actions] = use(fooModel);
actions.setValue(resp.data);
},
setLocalStorage(key: string, value: string) {
localStorage.set(key, value);
}
}
}
});

function App() {
const [, actions] = useModel(fooModel);

return <button type="button" onClick={async () => {
await actions.load();
actions.setLocalStorage();
}}>go</button>
}

PromiseEffect

一个异步请求完成前后通常有三种状态 pending(请求中)、fulfilled(请求成功)、rejected(请求失败)。

所以当触发一个 effect dispatcher 时,其内部就会在请求的不同阶段自行调用 actions.load.pending(请求中)、actions.load.fulfilled(请求成功调用)、actions.load.rejected(请求失败调用)这几个 action dispatcher

所以为了正常处理这这三个状态对应的结果,我们需要编写相应 pending、fulfilled、rejected 阶段的 action handler。在相应的 action handler 函数中处理副作用函数所触发的不同阶段。

示例
import { model, useModel } from '@modern-js/runtime/model';

const fooModel = model('foo').define((_, { use }) => {
return {
state: {
items: [{
text: 'Use Reduck',
completed: false
}],
isPending: false,
error: null
},
actions: {
load: {
pending(state) {
state.isPending = true;
state.error = null;
},
fulfilled(state, payload: Array<{ text: string; completed: boolean }>) {
state.items = payload;
state.isPending = false;
},
rejected(state, error: any) {
state.error = error;
state.isPending = false;
}
}
},
effects: {
async load() {
return await [
{ text: 'foo', completed: false},
{ text: 'bar', completed: true }
]
},
}
}
});

function App() {
const [, actions] = useModel(fooModel);

return <button type="button" onClick={async () => {
await actions.load();
actions.setLocalStorage();
}}>go</button>
}

ThunkEffect

redux-thunk 中间件也是为了解决异步函数诞生的。所以这里也支持了 Thunk 类型函数

提示

支持返回值为 Promise<any>,当返回值为 Promise 时,效果等同于 PromiseEffect,也会主动调用各个状态的 action dispatcher,这样无需手动调用了。

示例
import { model, useModel } from '@modern-js/runtime/model';

const fooModel = model('foo').define((_, { use }) => {
return {
state: {
value: 'foo'
},
effects: {
loadThunk: (...args) => (dispatch, getState) => {
dispatch.todo.load.pending()
/* 等价于
dispatch({
type: 'TODO/LOAD/PENDING'
})
*/

fetch('xxxx')
.then(data => context.actions.load.fulfilled(data))
.catch(err => context.actions.load.rejected(err))
}
}
}
});

function App() {
const [, actions] = useModel(fooModel);

return <button type="button" onClick={() => {
actions.load();
}}>go</button>
}