用 redux-toolkit 重写 redux-orm reducer [英] Rewrite redux-orm reducer with redux-toolkit
问题描述
我们如何创建一个自定义redux-orm reducer 和 redux-toolkit 的 createSlice?
How can we create a custom redux-orm reducer with redux-toolkit's createSlice?
是否有比此问题中提供的尝试更简单、推荐、更优雅或只是其他的解决方案?
Is there a simpler, recommended, more elegant or just other solution than the attempt provided in this question?
一个自定义redux的例子-orm reducer 看起来如下(简化):
The example of a custom redux-orm reducer looks as follows (simplified):
function ormReducer(dbState, action) {
const session = orm.session(dbState);
const { Book } = session;
switch (action.type) {
case 'CREATE_BOOK':
Book.create(action.payload);
break;
case 'REMOVE_AUTHOR_FROM_BOOK':
Book.withId(action.payload.bookId).authors.remove(action.payload.authorId);
break;
case 'ASSIGN_PUBLISHER':
Book.withId(action.payload.bookId).publisherId = action.payload.publisherId;
break;
}
return session.state;
}
可以使用 createSlice
redux-toolkit 的功能(基于redux-toolkit 使用指南):
It's possible to simplify reducers with the createSlice
function of redux-toolkit (based on the redux-toolkit usage-guide):
const ormSlice = createSlice({
name: 'orm',
initialState: [],
reducers: {
createBook(state, action) {},
removeAuthorFromBook(state, action) {},
assignPublisher(state, action) {}
}
})
const { actions, reducer } = ormSlice
export const { createBook, removeAuthorsFromBook, assignPublisher } = actions
export default reducer
然而,在 redux-orm reducer 的开始我们需要创建一个会话
However, at the beginning of redux-orm reducer we need to create a session
const session = orm.session(dbState);
然后我们使用 redux-orm reducer 魔法,最后我们需要返回状态
then we do our redux-orm reducer magic, and at the end we need to return the state
return session.state;
因此,我们在 createSlice
中遗漏了诸如 beforeEachReducer
和 afterEachReducer
之类的方法来添加此功能.
So we miss something like beforeEachReducer
and afterEachReducer
methods in the createSlice
to add this functionality.
我们创建了一个 withSession
高阶函数,用于创建会话并返回新状态.
We created a withSession
higher-order function that creates the session and returns the new state.
const withSession = reducer => (state, action) => {
const session = orm.session(state);
reducer(session, action);
return session.state;
}
我们需要在这个withSession
中包装每一个reducer逻辑.
We need to wrap every reducer logic in this withSession
.
import { createSlice } from '@reduxjs/toolkit';
import orm from './models/orm'; // defined elsewhere
// also define or import withSession here
const ormSlice = createSlice({
name: 'orm',
initialState: orm.session().state, // we need to provide the initial state
reducers: {
createBook: withSession((session, action) => {
session.Book.create(action.payload);
}),
removeAuthorFromBook: withSession((session, action) => {
session.Book.withId(action.payload.bookId).authors.remove(action.payload.authorId);
}),
assignPublisher: withSession((session, action) => {
session.Book.withId(action.payload.bookId).publisherId = action.payload.publisherId;
}),
}
})
const { actions, reducer } = ormSlice
export const { createBook, removeAuthorsFromBook, assignPublisher } = actions
export default reducer
推荐答案
我遇到了这个问题,希望结合 redux-toolkit和 redux-orm.我能够想出一个解决方案我很高兴到目前为止.这是我的 redux-orm 模型的样子:
I came across this question looking to combine the benefits of redux-toolkit and redux-orm. I was able to come up with a solution I've been pretty happy with so far. Here is what my redux-orm model looks like:
class Book extends Model {
static modelName = 'Book';
// Declare your related fields.
static fields = {
id: attr(), // non-relational field for any value; optional but highly recommended
name: attr(),
// foreign key field
publisherId: fk({
to: 'Publisher',
as: 'publisher',
relatedName: 'books',
}),
authors: many('Author', 'books'),
};
static slice = createSlice({
name: 'BookSlice',
// The "state" (Book) is coming from the redux-orm reducer, and so will
// never be undefined; therefore, `initialState` is not needed.
initialState: undefined,
reducers: {
createBook(Book, action) {
Book.create(action.payload);
},
removeAuthorFromBook(Book, action) {
Book.withId(action.payload.bookId).authors.remove(action.payload.authorId);
},
assignPublisher(Book, action) {
Book.withId(action.payload.bookId).publisherId = action.payload.publisherId;
}
}
});
toString() {
return `Book: ${this.name}`;
}
// Declare any static or instance methods you need.
}
export default Book;
export const { createBook, removeAuthorFromBook, assignPublisher } = Book.slice.actions;
redux-toolkit 切片被创建为类上的静态属性,然后模型及其动作以类似于 Ducks 的方式导出(ORMDucks??).
The redux-toolkit slice is created as a static property on the class, and then the model and its actions are exported in a manner similar to Ducks (ORMDucks??).
唯一要做的其他修改是为redux-orm 的减速器:
The only other modification to make is to define a custom updater for redux-orm's reducer:
const ormReducer = createReducer(orm, function (session, action) {
session.sessionBoundModels.forEach(modelClass => {
if (typeof modelClass.slice.reducer === 'function') {
modelClass.slice.reducer(modelClass, action, session);
}
});
});
在此处查看更完整的示例:https://gist.github.com/JoshuaCWebDeveloper/25a302ec891acb6c460f>
See a more complete example here: https://gist.github.com/JoshuaCWebDeveloper/25a302ec891acb6c4992fe137736160f
- @markerikson 对 redux-toolkit 的一些特性提出了很好的观点未使用,因为 redux-orm 正在管理状态.对我来说,两个使用这种方法的最大好处是不必争论一个整体一堆动作创建者,不必与糟糕的
switch
抗衡声明 :D. - 我正在使用第 3 阶段的类字段和静态类功能提案.(看https://babeljs.io/docs/en/babel-plugin-提案类属性).使这个 ES6 兼容,你可以很容易地重构模型类来定义它的使用当前语法的静态道具(即
Book.modelName = 'Book';
). - 如果您决定将上述模型与未定义的模型混合使用一个
slice
,然后你需要调整createReducer
更新程序中的逻辑
- @markerikson makes a good point about some of the features of redux-toolkit
not being used since redux-orm is managing the state. For me, the two
greatest benefits of using this method are not having to wrangle a whole
bunch of action creators and not having to contend with awful
switch
statements :D. - I am using the stage 3 class fields and static class features proposals. (See
https://babeljs.io/docs/en/babel-plugin-proposal-class-properties). To make
this ES6 compatible, you can easily refactor the model class to define its
static props using the current syntax (i.e.
Book.modelName = 'Book';
). - If you decide to mix models like the one above with models that don't define
a
slice
, then you'll need to tweak the logic in thecreateReducer
updater slightly.
对于真实世界的示例,请在此处查看我如何在我的项目中使用模型:https://github.com/vallerancereact-orcus/blob/70a389000b6cb4a00793b723a25cac52f6da519b/src/redux/models/OrcusApp.js.该项目仍处于早期阶段.我心中最大的问题是这种方法的扩展性如何;然而,我乐观地认为它会继续随着我的项目的成熟,提供许多好处.
For a real world example, see how I use the model in my project here: https://github.com/vallerance/react-orcus/blob/70a389000b6cb4a00793b723a25cac52f6da519b/src/redux/models/OrcusApp.js. This project is still in the early stages. The largest question in my mind is how well this method will scale; however, I am optimistic that it will continue to provide numerous benefits as my project matures.
这篇关于用 redux-toolkit 重写 redux-orm reducer的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!