Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save JeroenNelen/b9f0a4399cfcaa637550551774ef4b73 to your computer and use it in GitHub Desktop.
Save JeroenNelen/b9f0a4399cfcaa637550551774ef4b73 to your computer and use it in GitHub Desktop.
state-normalizr-question
If I have an application that shows the green houses in a city, you load the application and I want to show all the green houses on a map so I do the necessary call and stick the data in my state under houses
state = {
houses: [arrayWithAllTheHouses]
}
now I also want to be able to show houses specific for an owner, imagine the owner has 3 green houses, I was thinking to put in my state:
state = {
houses: [arrayWithAllTheHouses]
selectedUser: {userDataFromTheSelectedUserNothingNested}
selectedUserHouses: [arrayWithAllTheHousesForTheUser]
}(edited)
[3:49 PM] devtist: damn, entered too fast, I'll edt
[3:49 PM] devtist: edit
[3:50 PM] devtist: initially, this seemed okay for me, but then I wanted to start writing the reducer for selectedUserHouses, but that seems to be a full copy of the general housesReducer? Because on fetch, request etc. it will do the same?
[3:53 PM] devtist: so I'm struggling with how that kind of situation would/should be handled:
long list with 1000+ objects
short list with <10 objects but of same type
for me I would think it would be best to just fetch the short list seperate from the long list, but then I run into stuff that feels weird like reducer that looks practically the same, or is that "okay" and not a problem?
[3:55 PM] devtist: (and I have an influence on the API, so we were discussing with the team to keep the data we get from the API, as flat as possible, because I thought: if we dont receive any (deeply) nested objects from the beginning, we won't run into big issues with normalizing, so that's why when I request the user I dont receive his greenHouses embedded e.g.)
[4:06 PM] devtist: (or is it just okay to have reducer logic that looks the same?, it seems more efficient to do a API call to fetch a user his houses compared to traversing a list of 1000+ houses to see which ID's are linked to the user?)
[4:06 PM] MaxSchmeling: I'm relatively new to redux, so don't take what I do as gospel, but I have an entities reducer and an action called MERGE_ENTITIES
[4:07 PM] MaxSchmeling: The MERGE_ENTITIES action gets dispatched from anywhere that does an API call that returns any of my model objects
[4:07 PM] MaxSchmeling: My redux state atom looks like { ..., entities: { users: { }, customers: { }, etc: { } } }
[4:08 PM] MaxSchmeling: And the entities reducer handles MERGE_ENTITIES and generically updates that part of the state with returned objects
[4:08 PM] MaxSchmeling: So it's a normalized database of any data that has been returned from the API
[4:09 PM] MaxSchmeling: Then I can store things like myCustomers as an array of IDs in a place that needs to use that request specific information
[4:09 PM] MaxSchmeling: So then only one reducer is dealing with storing entities out of normalized responses... and the other reducers are storing the result in a way that makes sense for the use case
[4:10 PM] devtist: hmm, I see, but do you have hudreds of customers in the state then?
[4:11 PM] MaxSchmeling: Possibly
[4:11 PM] MaxSchmeling: I haven't had to worry about that yet... but you can do cleanup of that state if it becomes a performance issue...
[4:11 PM] MaxSchmeling: but I wouldn't worry about it unless it really is a performance issue
[4:12 PM] devtist: I'm torn on what to do :p
[4:12 PM] MaxSchmeling: Are you worried about memory being used by the objects, or worried about access time? Because they're stored as an object keyed by ID so the access is super quick
[4:13 PM] devtist: I was thinking about making my state like this:
houses
selectedUser
selectedUserHouses
selectedUserVehicles
...
which every one of them containing entire objects (no ID's as I haven't applied any normalisation yet) and all of them have their own api call to be filled
[4:14 PM] MaxSchmeling: The typical redux advice is store your data in a normalized way. It's not a requirement, but typically advised
[4:15 PM] MaxSchmeling: http://redux.js.org/docs/basics/Reducers.html
[4:15 PM] MaxSchmeling: See the "Note on Relationships"
[4:16 PM] devtist: but it somewhat feels icky to let the API return nested results, just to then normalize it again in the front-end?
[4:16 PM] MaxSchmeling: Yeah, if the API could return the data in a normalized fashion that would be even better.
[4:16 PM] MaxSchmeling: It would send less data over the wire, and require less processing on the JS side
[4:17 PM] MaxSchmeling: json:api would probably be really great... but I haven't used it yet http://jsonapi.org/
[4:18 PM] devtist: that was what I was trying to reach, this way:
1) request a user, receive user without any nested houses or nested vehicles
2) make a request to get the houses from a user
3) make a request to get the vehicles from a user
so 3 API calls that send back un-nested objects
instead of 1 API call retuning nested objects
but now I'm wondering if I, even then, should still make the uuid's be keys for the objects in the state etc.
[4:19 PM] MaxSchmeling: You can make 1 API call that returns normalized objects
[4:19 PM] MaxSchmeling: Where it sends back what you would have gotten by processing through normalizr
[4:19 PM] MaxSchmeling: or something similiar
[4:21 PM] MaxSchmeling:
{
result: {
"user": {
id: "user id",
houses: [ "id1", "id2 ],
vehicles: [ "id3", "id4" ]
}
},
houses: { "id1": { }, "id2": { } },
vehicles: { "id3": { }, "id4": { } }
}
(edited)
[4:22 PM] devtist: hmm
[4:23 PM] devtist: and what do you think about building the same in different calls:
1) first call returns this:
"user": {
id: "user id",
houses: [ "id1", "id2 ],
vehicles: [ "id3", "id4" ]
}
then I do additional calls for the houses/vehicles
[5:03 PM] MaxSchmeling: It's less efficient, but still fine
[5:03 PM] MaxSchmeling: If you do that first, and the other calls second, you'll have a state where you have house and vehicle IDs that don't have corresponding objects loaded
[5:03 PM] MaxSchmeling: until the subsequent calls complete
[5:06 PM] naw: Just sharing an opinion here: I would retrieve the huge list of houses and store it as a hash. Then I would retrieve the user with his housIds. Then I would map over the houseIds to get the actual house objects from the huge list.
[5:11 PM] naw: However, if you're stuck on having two separate slices of state holding houses, that's fine --- you can remove duplication in your reducers by constructing them with a factory --- (i.e. a function that returns a reducer)
[5:12 PM] devtist: thanks for all the input
[5:13 PM] naw: Also, depending on how you're using the data on the page, it might be fine just to keep the houses nested under the user ---- https://github.com/reactjs/redux/issues/1824#issuecomment-227910282
[5:17 PM] devtist: thanks
[5:19 PM] devtist: so, this approach is lacking?
{
result: {
"user": {
id: "user id",
}
},
userHouses: [],
userVehicles: []
allHouses []
}(edited)
[5:20 PM] naw: is that an API Response?
[5:21 PM] devtist: no, this would be created by doing the following (when needed/required):
1) api call GET USER
2) api call GET houses/userId
3) api call GET vehicles/userId
4) api call GET vehicles (all)(edited)
[5:22 PM] devtist: because I was all like :p, I dont want anything nested, otherwise I have to normalize at the front-end, which seemed stupid if we could keep the data flat this way :(
[5:23 PM] naw: So the ideal redux state shape depends on how you're using the data on the page. Once you decide what state shape you want, then you can figure out what API calls will most easily help you construct it (assuming you have some control over the API)
[5:26 PM] naw: You have to ask yourself what kind of operations you're performing on the state once it's loaded.
[5:26 PM] naw: Or, what ways you're fetching data out of the state
[5:29 PM] naw: Regarding arrays/hashes --- if you're just iterating over the data to display it, arrays are great. However, if you ever need to reference an element by ID, hashes are usually better. For example, if you have a bunch of markers on a map for houses, and you want to click a marker to load a box with extra information on that house, you're likely referencing the house by ID, which means having a hash of houses would be better than an array of houses.
[5:42 PM] devtist: yes, I also read your comment and understand that having duplicate data in the state with update options might lead to inconsistency
[5:51 PM] devtist: maybe I'm overthinking it
[5:55 PM] devtist: but it's okay to have the same type of objects spread in different places in the state, especially if they're not duplicated anywhere?
e.g. state:
{
greenHouses: [array or has with house objects]
blueHouses: [array or has with house objects]
pinkHouses: : [array or has with house objects]
}
[5:56 PM] MaxSchmeling: If they aren't duplicated, sure... easier to gaurantee that and simpler to modify in the future if they aren't in different places though
[5:56 PM] MaxSchmeling: But it's up to your use case how you structure it
[6:00 PM] devtist: yes, I think that it's inevitable that we will have to change some things along the way, there are still quite some things unclear
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment