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

And*_*gan 6 javascript architecture flux reactjs redux

问题:

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

我的看法:

目前的趋势似乎是围绕相关的容器组件组织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
Run Code Online (Sandbox Code Playgroud)

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

问题:

当我们需要访问时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,
        }
    },
}   
Run Code Online (Sandbox Code Playgroud)

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

这个用例的主要例子是身份验证(啊...我们确实喜欢讨厌你,因为它是一个非常常见的"副作用"模型).我们经常需要/Auth在整个应用程序中访问传奇,动作和选择器.我们可能有集装箱/PasswordRecover,/PasswordReset,/Login,/Signup....其实在我们的应用程序我们/Authcontianer没有实际分量可言!

/src
    /contianers
        /Auth
            actions.js
            constants.js
            reducer.js
            selectors.js
            sagas.js
Run Code Online (Sandbox Code Playgroud)

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

Pie*_*scy 5

我个人使用ducks-modular-redux提议.

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

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

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

containers/文件夹包含从上面的纯组件创建的容器 当一个容器需要一个selector涉及许多"ducks" 的特定容器时,我将它写在我编写该<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
Run Code Online (Sandbox Code Playgroud)

它让我们"隐藏"每个ducks 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
Run Code Online (Sandbox Code Playgroud)

当我的"鸭子"足够小,我经常跳过的文件夹创建并直接写入一个ducks/Books.js或一个ducks/Taxonomies.js文件,所有这5个文件(actionTypes.js,actionCreators.js,reducers.js,selectors.js,sagas.js)合并在一起.