Redux:组织容器、组件、动作和减速器

6

问题:

在一个大型React/Redux应用中,组织容器、组件、操作和减速器的最可维护和推荐的最佳实践是什么?

我的观点:

目前的趋势似乎是围绕相关的容器组件来组织redux的附属内容(操作、减速器、saga等)。

/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、selectors 或 sagas 时,似乎会出现反模式。假设我们有一个全局的 /App 容器,其中包含一个 reducer/state,存储我们在整个应用程序中使用的信息,如类别和枚举。继续上面的示例,有一个状态树:
{
    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 sagas、actions 和 selectors。我们可能有容器/PasswordRecover/PasswordReset/Login/Signup.... 实际上,在我们的应用程序中,我们的/Auth容器根本没有实际组件!
/src
    /contianers
        /Auth
            actions.js
            constants.js
            reducer.js
            selectors.js
            sagas.js

这只是包含了所有Redux文物的容器,用于各种不同的、常常不相关的身份验证容器。


我很好奇,根据你目前的结构,你是如何使用你的选择器的?比如一个组件使用了 BookList 的选择器函数,你能给我展示一下你的 mapStateToProps 函数吗?你是通过传递整个 state 还是只传递 state.booklist - xiaofan2406
1个回答

5
我个人使用ducks-modular-redux提案。
这不是“官方”推荐的方式,但对我非常有效。每个“duck”包含一个actionTypes.jsactionCreators.jsreducers.jssagas.jsselectors.js文件。这些文件中没有对其他“duck”的依赖,以避免循环依赖或鸭子圈,每个“duck”仅包含其必须管理的逻辑。
然后,在根目录下有一个componentscontainers文件夹以及一些根文件: components/文件夹包含我应用程序的所有纯组件。

containers/文件夹包含从上面的纯组件创建的容器。当容器需要一个涉及许多“ducks”的特定selector时,我将其写入与我编写<Container/>组件相同的文件中,因为它与此特定容器相关。如果selector在多个容器之间共享,则在单独的文件中创建它(或在提供这些属性的HoC中创建它)。

rootReducers.js:简单地通过组合所有reducer公开根reducer。

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

它让我们在每个 duck 的 selectors.js 文件中“隐藏”州形状。由于每个选择器都在 ducks 中接收整个状态,因此您只需在 selector.js 文件中导入相应的 rootSelector

rootSagas.js 组合了所有鸭子内部的 saga,并处理涉及多个“鸭子”的复杂流程。

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

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”足够小的时候,我经常跳过文件夹创建步骤,直接编写一个包含所有这5个文件(actionTypes.jsactionCreators.jsreducers.jsselectors.jssagas.js)合并在一起的ducks/Books.jsducks/Taxonomies.js文件。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接