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

时间:2016-07-14 23:42:06

标签: javascript reactjs architecture redux flux

问题:

  

在大型组织中组织容器,组件,操作和减速器的最可维护和建议的最佳实践是什么   React/Redux申请?

我的观点:

目前的趋势似乎是在相关容器组件周围组织redux抵押品(行动,减速器,传奇......)。 e.g。

/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

这很棒!虽然这种设计似乎存在一些问题。

问题:

当我们需要从另一个容器访问actionsselectorssagas时,它似乎是一种反模式。假设我们有一个带有reducer / state的全局/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,
        }
    },
}   

如果我们要在selector容器中的/App容器中使用/BookList,我们需要在/BookList/selectors.js中重新创建它(肯定是错的?)或导入它来自/App/selectors(它总是与EXACT相同的选择器吗??不。)。这些appraoches对我来说都不是最理想的。

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

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

简单地包含上面提到的各种且通常不相关的auth容器的所有Redux附属品。

1 个答案:

答案 0 :(得分:5)

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

这不是“官方”推荐方式,但对我来说效果很好。每个“duck”包含actionTypes.jsactionCreators.jsreducers.jssagas.jsselectors.js个文件。这些文件中没有依赖于其他ducks来避免循环依赖或duck circle,每个“duck”只包含它必须管理的逻辑。

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

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

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

rootReducers.js :通过组合所有reducer来简单地公开root 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文件中的状态形状。由于每个selector都会在您的鸭子中收到整个州,因此您只需在rootSelector个文件中导入相应的selector.js

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

当我的“鸭子”足够小时,我经常跳过文件夹创建并直接写一个包含所有这5个文件的ducks/Books.jsducks/Taxonomies.js文件(actionTypes.js,{{1 }},actionCreators.jsreducers.jsselectors.js)合并在一起。