先放 combineModels 实现:
实现比较简单:
const combineModels = (...models) =>
models.reduce((sum, item) => ({
...sum,
...item,
state: {
...sum.state,
...item.state
},
reducers: {
...sum.reducers,
...item.reducers
},
effects: {
...sum.effects,
...item.effects
},
subscriptions: {
...sum.subscriptions,
...item.subscriptions
}
}), {} );
意图:创建一个函数 combineModels,可以合并多个 model。也便于拆分比较庞大的 model
比如现在有两个 model(没有指定 namespace):
const modelLoading = {
state: {
loading: false
},
reducers: {
loadingChange: (state, { loading }) => ({
...state,
loading
})
}
};
const modelSubmitting = {
state: {
submitting: false
},
reducers: {
submittingChange: (state, { submitting }) => ({
...state,
submitting
})
}
};
将 modelLoading 与 modelSubmitting 合并
const totalModel = combineModels(
modelLoading,
modelSubmitting,
{ namespace: "foo" } // 指定一下namespace
);
那么得到一个合并后的 totalModel 对象, 维护了 loading 和 submitting 两个状态, totalModel 如下:
{
namespace: "foo",
state: {
loading: false,
submitting: false
},
reducers: {
loadingChange: (state, { loading }) => ({
...state,
loading
}),
submittingChange: (state, { submitting }) => ({
...state,
submitting
})
}
};
拆分大型 model
一般做项目的时候一个 page 对应一个 model,管理的状态会比较多而杂乱
这时候可以将大型的 Model(比如 totalModel)分解成小型的 model(modelLoading,modelSubmitting)
对相似的小型 model 再进行重构
创建管理单一变量的 model
const createSingleStateModel = (name, initValue) => ({
state: {
[name]: initValue
},
reducers: {
[`${name}Change`]: (state, action) => ({
...state,
[name]: action[name]
})
}
});
const modelLoading = createSingleStateModel('loading', false);
const modelSubmitting = createSingleStateModel('submitting', false);
const modelCount = createSingleStateModel('count', 0);
一个比较实际的案例
原来的 model
const apiLoadData = () =>
new Promise(r =>
r({
data: []
})
);
const apiSubmit = () => new Promise(r => r({}));
const fooModel = {
namespace: "foo",
state: {
loading: false,
data: [],
submitting: false
},
effects: {
*loadData(action, { call, put }) {
yield put({ type: "loadingOn" });
try {
const data = yield call(apiLoadData);
yield put({
type: "dataChange",
data
});
} catch (e) {
} finally {
yield put({ type: "loadingOff" });
}
},
*submit(action, { call, put }) {
yield put({ type: "submittingOn" });
try {
const data = yield call(apiSubmit);
} catch (e) {
} finally {
yield put({ type: "submittingOff" });
}
}
},
reducers: {
loadingOn: (state, action) => ({
...state,
loading: true
}),
loadingOff: (state, action) => ({
...state,
loading: false
}),
submittingOn: (state, action) => ({
...state,
submitting: true
}),
submittingOff: (state, action) => ({
...state,
submitting: false
}),
dataChange: (state, { data }) => ({
...state,
data
})
}
};
提取 createDoingModel
const createDoingModel = (name) => ({
state: {
[name]: false
},
reducers: {
[`${name}On`]: (state, action) => ({
...state,
[name]: true
}),
[`${name}Off`]: (state, action) => ({
...state,
[name]: false
})
}
});
结合 createSingleStateModel, 那么 fooModal 就变成了
const fooModel = combineModels(
createDoingModel("loading"),
createDoingModel("submitting"),
createSingleStateModel("data", []),
{
namespace: "foo",
effects: {
*loadData(action, { call, put }) {
yield put({ type: "loadingOn" });
try {
const data = yield call(apiLoadData);
yield put({
type: "dataChange",
data
});
} catch (e) {
} finally {
yield put({ type: "loadingOff" });
}
},
*submit(action, { call, put }) {
yield put({ type: "submittingOn" });
try {
const data = yield call(apiSubmit);
} catch (e) {
} finally {
yield put({ type: "submittingOff" });
}
}
}
}
);
把数据加载和提交逻辑分开
const loadDataModel = combineModels(
createDoingModel("loading"),
createSingleStateModel("data", []),
{
effects: {
*loadData(action, { call, put }) {
yield put({ type: "loadingOn" });
try {
const data = yield call(apiLoadData);
yield put({
type: "dataChange",
data
});
} catch (e) {
} finally {
yield put({ type: "loadingOff" });
}
}
}
}
);
const submitModel = combineModels(
createDoingModel("submitting"),
{
effects: {
*submit(action, { call, put }) {
yield put({ type: "submittingOn" });
try {
const data = yield call(apiSubmit);
} catch (e) {
} finally {
yield put({ type: "submittingOff" });
}
}
}
}
);
const fooModel = combineModels(
loadDataModel,
submitModel,
{ namespace: "foo" }
);