reduxjs / redux

A JS library for predictable global state management
https://redux.js.org
MIT License
60.88k stars 15.27k forks source link

Why did I denormalize .. now it is so hard to do anything! #2801

Closed pstanton closed 6 years ago

pstanton commented 6 years ago

Forgive the dramatic title. I (like many past, present and future) am midway through this mental exercise that is learning how to operate in the recommended react-native-redux-normalizr stack.

I'm slowly starting to grasp how things are done, but feel the benefits are still over some unknown horizon. My current gripe is that I'm struggling with a very simple application, which lists "installations" (and allows you to pick one).

There are only two complications:

  1. the list is separated into two categories
  2. the normally nested entity "user" is also required for the display

Before, I had an array of installations, with nested data for the allocated user (sure, duplication would be an issue).

{
    installations: [
        {id:1, user: {id:1, name:"joe", ...}, assigned:true, ...}, ...
    ],
}

And I could have easily just filtered this array, twice:

let assigned = installations.filter(i => { return i.assigned; });
let unassigned = installations.filter(i => { return !i.assigned; });

then when rendering, access to the "user" attribute would be implicit.

assigned.map(i => { <Text>{i.user.name}</Text> });

so, in all, 3 lines (for comparison sake).

Fast forward to where I'm trying to do the same "the right way", I have set up my normalized data:

{
        installations: {
            1: {id:1, user:1, assigned:true, ...}, ...
        },
        users: {
            1: {id:1, name:"joe", ...}, ...
        }
    }
}

My reducers are configured and I'm "connecting" my app.

const mapStateToProps = (state, ownProps) => {
    let assignedInstallations = Object.keys(state.installations)
        .filter(i => {
            return state.installations[i].assigned;
        })
        .map(i => {
            return state.installations[i];
        });
    let unassignedInstallations = Object.keys(state.installations)
        .filter(i => {
            return !state.installations[i].assigned;
        })
        .map(i => {
            return state.installations[i];
        });
    return {
        assignedInstallations,
        unassignedInstallations,
        users: state.users
    };
};

and then while rendering:

let user = this.props.users[installation.user];

Note that the filtering is FAR more complex, to write and to process, and I also have to connect the whole list of users even though I may only end up using one when rendering - I assume this means if any user changes, the whole list re-renders.

I feel I would like to de-normalize the data prior to display, but wouldn't that break the link between rendering logic and redux?

Please tell me I'm doing this wrong because if this is right, I see a bit of a downturn in productivity, and I haven't even started modifying data yet.

pstanton commented 6 years ago

I've learnt, the filtering can be simplified using lodash.filter

let assignedInstallations = filter(state.installations, installation => installation.assigned);
let unassignedInstallations = filter(state.installations, installation => !installation.assigned);

What of the 2nd issue of referenced entities?

timdorr commented 6 years ago

This is a bug tracker, not a support system. For usage questions, please use Stack Overflow or Reactiflux where there are a lot more people ready to help you out. Thanks!