简体   繁体   中英

Structuring state for a react/redux app

I'm struggling with structuring a React/Redux application - I'm listing out the problem with options I tried for solutions, but nothing "feels right", so hoping someone here could help me out.

Here's a rough idea of my component structure:

<Dashboard>
    <Widget1 dataFetcher=()=>{}>
        <Header>
            <Title> ... </Title>
            <Menu>
                <MenuItem {..cosmeticProps} text="OpenSettings" onClick=handleSettingsOpen>
                <MenuItem {..cosmeticProps} text="Delete" onClick=handleWidgetDelete>
            </Menu>
        </Header>
        <Body>
            <Settings isOpen isValid fields onValidate onAutoComplete.. </Settings>
            { ifError ? ErrorLayout}
            { ifFetching ? FetchingLayout }
            { ifValid ? DataLayout }
        </Body>
    </Widget1>
    ...
</Dashboard>

And here's the state structure (event handlers shown for completeness, not because they're explicitly part of the state)

Dash: {
    widgets: {
        widget1: {
            menu: {
                isOpen: true,

                handleSettingsOpen: ()=>{}
                handleWidgetDelete: ()=>{}
            }
            settings: {
                isOpen: true,
                isValid: true,
                fields: [...],

                onValidate: ()=>{},
                onAutoComplete:()=>{},
                onSave:()=>{}
            }
            data: {
                isFetching: false,
                isError: false,
                items: [],

                fetch: ()=>{}
                parse: ()=>{}
            }
        }
        ...
    }
}

Option 1:

Connect the dashboard and let it pass to children as required. ie,

Connected-dashboard.js

stateToProps ()=> { widgets: state.widgets }
dispatchToProps ()=> { handleSettingsOpen, handleWidgetDelete handleSettingsSave ... } //Dashboard would bind these with moduleid while rendering
  • Pro: Everything else can be 'dumb', single source of truth
  • Con: Knows too much about state, list of props/dispatches it takes just to pass down makes for ugly reading

Option 2:

Build a 'connected' widget and use that in the dashboard.

connected-widget.js

stateToProps ()=> { state.widgets[props.widgetid] }
dispatchToProps ()=> { handleSettingsOpen, handleWidgetDelete handleSettingsSave ... }
  • Pro: Dashboard can now be a dumb container, which it is anyway
  • Con: Widget knows too much about state structure?

Option 3: Build connected versions of individual components and assemble later

connected-menu.js

stateToProps ()=> { state.widgets[props.widgetid].menu }
dispatchToProps ()=> { handleSettingsOpen, handleWidgetDelete }

connected-settings.js

stateToProps ()=> { state.widgets[props.widgetid].settings }
dispatchToProps ()=> { handleSave, handleValidate }
  • Pro: Every component gets exactly the slice of state it cares about
  • Con: Too many components listening on the state? Also the question of who 'assembles' it.

Option 3.1: Restructure state to be:

Dashboard: {
    widgets: { ..}
    menu: {widgetid: {isopen ..}}
    settings: {widgetid: {widgetid ..}}
}

(State is flatter with this approach, but not sure if it matters much)

Overall, this may be naive/obvious, but to me the trade-off seems to be having a parent which either knows too much about the state, or too much about how it's children are put-together. How would you approach this?

Option 3: Does it make sense for Menu and Settings to know "widgetId"? It seems they would be more reusable if they simply receive the properties menu or settings respectively.

Option 1: Do you want to update Dashboard stateToProps and dispatchToProps for each widget component supported?

For these reasons, I like option 2, the connected Widget1.

As for state nesting depth, Redux Async Actions has a "Note on Nested Entities" that suggests avoiding deeply nested entities to avoid duplicate data.

In your example, if any widgets had duplicate menu or settings state object, a normalized state would allow the widgets to share the same state.

Dashboard: {
    widgets: {
        widget1: {menuId:1, settingsId: 1, ...},
        widget2: {menuId:1, settingsId: 1, ...},
    },
    menus: {1: {...}},
    settings: {1: {...}}
}

Actually, with this structure, Menu and Settings only need to know menuId or settingsId, not widgetId. I still prefer connecting the widget though.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM