用 redux-toolkit 重写 redux-orm reducer [英] Rewrite redux-orm reducer with redux-toolkit

查看:65
本文介绍了用 redux-toolkit 重写 redux-orm reducer的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们如何创建一个自定义redux-orm reducerredux-toolkitcreateSlice?

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 中遗漏了诸如 beforeEachReducerafterEachReducer 之类的方法来添加此功能.

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-toolkitredux-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 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 the createReducer 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屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆