Redux:组织容器、组件、动作和化简器

Redux: organising containers, components, actions and reducers

本文关键字:组件 Redux      更新时间:2023-09-26

问题:

大型中组织容器、组件、动作和减速器的最可维护和推荐的最佳实践是什么 React/Redux application?

我的意见:

目前的趋势似乎是围绕相关的容器组件组织redux抵押品(动作,化简器,传奇......)。

/src
    /components
        /...
    /contianers
        /BookList
            actions.js
            constants.js
            reducer.js
            selectors.js
            sagas.js
            index.js
        /BookSingle
            actions.js
            constants.js
            reducer.js
            selectors.js
            sagas.js
            index.js        
    app.js
    routes.js

这很好用!尽管此设计似乎存在一些问题。

问题:

当我们需要访问 actions ,从另一个容器selectorssagas,这似乎是一种反模式。假设我们有一个全局/App容器,其中包含一个化简器/状态,用于存储我们在整个应用中使用的信息,例如类别和可枚举项。从上面的示例开始,使用状态树:

{
    app: {
        taxonomies: {
            genres: [genre, genre, genre],
            year: [year, year, year],
            subject: [subject,subject,subject],
        }   
    }
    books: {
        entities: {
            books: [book, book, book, book],
            chapters: [chapter, chapter, chapter],
            authors: [author,author,author],
        }
    },
    book: {
        entities: {
            book: book,
            chapters: [chapter, chapter, chapter],
            author: author,
        }
    },
}   

如果我们想在/BookList容器中使用/App容器中的selector,我们需要在/BookList/selectors.js中重新创建它(肯定是错误的?或者从/App/selectors导入它(它将始终是完全相同的选择器..?这两种掌声对我来说似乎都不是最佳的。

这个用例的主要例子是身份验证(啊...我们确实喜欢讨厌你),因为它是一个非常常见的"副作用"模型。我们经常需要在整个应用程序中访问/Auth传奇、操作和选择器。我们可能有容器/PasswordRecover/PasswordReset/Login/Signup....实际上,在我们的应用程序中,我们的/Auth contianer 根本没有实际组件!

/src
    /contianers
        /Auth
            actions.js
            constants.js
            reducer.js
            selectors.js
            sagas.js

简单地包含上面提到的各种通常不相关的身份验证容器的所有 Redux 抵押品。

我个人使用ducks-modular-redux提案。

这不是"官方"推荐的方式,但它对我来说效果很好。每个"鸭子"包含一个actionTypes.jsactionCreators.jsreducers.jssagas.jsselectors.js文件。为了避免循环依赖或鸭子圈,这些文件没有对其他鸭子的依赖,每个"鸭子"只包含它必须管理的逻辑。

然后,在根目录上,我有一个components和一个containers文件夹以及一些根文件:

components/文件夹包含我的应用程序的所有纯组件

containers/文件夹包含从上述纯组件创建的容器。当容器需要涉及许多"鸭子"的特定selector时,我将其写入编写<Container/>组件的同一文件中,因为它是相对于该特定容器的。如果selector在多个容器中共享,我会在单独的文件中(或在提供这些 props 的 HoC 中创建它)。

rootReducers.js : 通过组合所有化简器来简单地暴露根化简器

rootSelectors.js公开每个状态切片的根选择器,例如,在您的情况下,您可以有类似以下内容:

/* let's consider this state shape
state = {
    books: {
        items: {  // id ordered book items
            ...
        }
    },
    taxonomies: {
        items: {  // id ordered taxonomy items
            ...
        }
    }
}
*/
export const getBooksRoot = (state) => state.books
export const getTaxonomiesRoot = (state) => state.taxonomies

它让我们在每个鸭子selectors.js文件中"隐藏"状态形状。由于每个selector都会接收鸭子内部的整个状态,因此您只需在selector.js文件中导入相应的rootSelector即可。

rootSagas.js组成鸭子内部的所有传奇故事,并管理涉及许多"鸭子"的复杂流程。

所以在您的情况下,结构可能是:

components/
containers/
ducks/
    Books/
        actionTypes.js
        actionCreators.js
        reducers.js
        selectors.js
        sagas.js
    Taxonomies/
        actionTypes.js
        actionCreators.js
        reducers.js
        selectors.js
        sagas.js
rootSelectors.js
rootReducers.js
rootSagas.js

当我的"鸭子"足够小时,我经常跳过文件夹创建,直接写一个ducks/Books.jsducks/Taxonomies.js文件,将所有这5个文件(actionTypes.jsactionCreators.jsreducers.jsselectors.jssagas.js)合并在一起。