5

The thing that has puzzled me the most ever since I started reading about SPAs is how to handle the application state.

Although I have found in Redux most of the answers I was looking for there's still a couple of things that I'm not sure how to handle.

One of those things is how to specify the shape of the "entities" I want to use in my app. In other frameworks I've used in the past I've seen an extensive use of ES2015 classes for this purpose, but in React/Redux applications object literals are by far,the preferred way to express the application state. Last night I was reading about normalizing data in the redux docs, and although it gave me great ideas about how to deal with deeply nested objects, it left me with the feeling that there was something missing and that thing is how to specify in a single spot and as clear as possible what the structure/shape of the state of the application is.

Take the sample data used in the article mentioned above:

{
    posts : {
        byId : {
            "post1" : {
                id : "post1",
                author : "user1",
                body : "......",
                comments : ["comment1", "comment2"]    
            },
            "post2" : {
                id : "post2",
                author : "user2",
                body : "......",
                comments : ["comment3", "comment4", "comment5"]    
            }
        }
        allIds : ["post1", "post2"]
    },
    comments : {
        byId : {
            "comment1" : {
                id : "comment1",
                author : "user2",
                comment : ".....",
            },
            "comment2" : {
                id : "comment2",
                author : "user3",
                comment : ".....",
            },
            "comment3" : {
                id : "comment3",
                author : "user3",
                comment : ".....",
            },
            "comment4" : {
                id : "comment4",
                author : "user1",
                comment : ".....",
            },
            "comment5" : {
                id : "comment5",
                author : "user3",
                comment : ".....",
            },
        },
        allIds : ["comment1", "comment2", "comment3", "commment4", "comment5"]
    },
    users : {
        byId : {
            "user1" : {
                username : "user1",
                name : "User 1",
            }
            "user2" : {
                username : "user2",
                name : "User 2",
            }
            "user3" : {
                username : "user3",
                name : "User 3",
            }
        },
        allIds : ["user1", "user2", "user3"]
    }
}

How would you express the shape of this data? Just like this?

{
    posts : {

    },
    comments : {

    },
    users : {

    }
}

Or perhaps:

{
    posts : {
        byId : {

        }
        allIds : []
    },
    comments : {
        byId : {

        }
        allIds : []
    },
    users : {
       byId : {

        }
        allIds : []
    }
}

Well, that tells me nothing about the actual shape of the objects that live inside each of the byId objects and that's exactly what has been bothering me since I started studying a little bit of Redux.

So the question is, is there any way/common practice that would let me express in the clearest possible way, the shape, specifically each of the properties of the objects that make up the state that the store is going to manage?

eddy
  • 4,373
  • 16
  • 60
  • 94
  • Using typescript works really well for this: you define the types for each object that you are putting in redux state, you end up with a spec for everything that can exist in the state. Really powerful in practice. (commenting instead of answering, as the question was about javascript) – OlliM Jun 28 '17 at 13:38
  • "is there any way/common practice that would let me express ... the shape" Express to whom? Are you trying to solve a technical problem or a human problem? – Jordan Running Jun 28 '17 at 15:13
  • @JordanRunning C'mon!! The person who originally created the app is not always going to be around, so IMHO I think it's a good idea to be as explicit as possible whenever you write a piece of code – eddy Jun 28 '17 at 15:16
  • @eddy Yes, I agree. That's why I'm asking. Your question doesn't indicate what your end goal is. If you're trying to solve a human problem, your question should indicate that. If you're trying to solve a technical problem, your question should indicate that. "How do I 'express' this?" could mean either of those things. – Jordan Running Jun 28 '17 at 16:01
  • @JordanRunning Sorry about that. Perhaps because English is not my mother tongue, I'm not aware of that subtle difference. – eddy Jun 28 '17 at 16:08

3 Answers3

3

As author of that "Structuring Reducers" docs section, that's because the actual content of your data items is entirely dependent on you and your application. This is really more of a generic Javascript "how do I express types?" question. There's static type systems like TypeScript and Flow; documentation-based approaches like JSDoc; runtime approaches like the React PropTypes library or tcomb; and schema-based approaches like JSON-Schema. Any of those approaches are valid ways to document and enforce data type definitions within an application.

Normalization is simply an approach for organizing those values, regardless of how you've chosen to define what their contents are.

markerikson
  • 63,178
  • 10
  • 141
  • 157
  • Yes, I guess you're right! What I was/am trying to do is to document the data type of the entities that make up my application's store. Now you mention PropTypes (I want to avoid TypeScript since I'm already using babel). With PropTypes, all I can think of is validating the structure of the objects in the Container Components, but that way every time I need to make use of a specific slice of state, I will have to specify PropTypes again.So my question is, if you had to document your state in the way I've described, where would you do it and what would you use for that purpose? – eddy Jun 28 '17 at 16:39
  • To be honest, I haven't actually tried using any of those approaches myself, other than JSON-Schema documentation for our APIs. So, afraid I don't have much advice to offer on this topic. Might at least be worth looking at Flow types, though. – markerikson Jun 28 '17 at 17:43
2

Assuming you're using plain JS, the simplest way is to use propTypes.shape.

 // An object taking on a particular shape
  optionalObjectWithShape: PropTypes.shape({
    color: PropTypes.string,
    fontSize: PropTypes.number
  }),

If you're using propTypes, I suggest declaring the shape of all objects in this manner.

Benefits:

  1. Your object shapes are explicit.
  2. You're alerted quickly when unexpected object shapes are passed around.
  3. You get enhanced autocompletion support in modern editors like Webstorm.
  4. Since the object shape is just an object, you can centralize the declaration to avoid repeating yourself.
  5. You don't need to move to TypeScript, Flow, etc. to have explicit types and basic type safety at runtime.
Cory House
  • 14,235
  • 13
  • 70
  • 87
0

The nice thing of Redux is that you have a very clear separation of concerns of the state. Every reducer is responsible of its own part of the state and if you end up to extend a reducer by adding more data/logic, you just need to breakdown that reducer in smaller reducers.

Generally, I would avoid the use of arrays inside big collections of data inside a reducer. Arrays are cool and gives you lot of nice functionalities (sorting, filtering, reducing, etc.) but looping through them is expensive. If you can, use objects and use an ID to identify your object, in this way you access to the object using the fastest way possible.

Using your example, I would use the first approach:

{
  posts: {
    1554: {},
    1557: {}
  },
  comments: {},
  users: {},
}

In case you need to keep track of the list objects the array, you just creates two new reducers inside your posts reducer:

// postsReducer.js
import byId from 'postsByIdReducer'
import allIds from 'allIdsReducer'

export default combineReducers({
  byId,
  allIds
})

So your state will be like

{
  posts: {
    byIds: {},
    allIds: []
  }
}
Ematipico
  • 1,181
  • 1
  • 9
  • 14
  • 1
    Yes I get your point and that's exactly how I plan to split my reducers, but what I need is to find a way to express the shape of each the entities in state. In a simple application like the classical todoapp, all I do is to define a initialState.js file and in there specify the basic structure of the state: `{ visibilityFilter: 'SHOW_ALL', todos: [] }` and the pass that object as the second parameter of `createStore` Unfortunately even in that simple example I will have no idea what properties a single todo has. – eddy Jun 28 '17 at 14:58
  • The shape of your state depends on the application and how you want to tackle different business requirement. There is **no standard** way to shape your state. There are pattern. Is your application going to be flat? Is is going to be deep? Like: posts -> post -> sharedMedia -> likes (example). The more you go deep, it's better to create a new level of reducers – Ematipico Jun 28 '17 at 15:07
  • 1
    There's definitely going to be several levels of nesting. That's why I found the articles about Normalizr really enlightening. What's bothering me is that all the examples I have seen, seem to sort of define the structure the object within the action creator when they specify the action payload. – eddy Jun 28 '17 at 15:19
  • The *trick* is to maintain the data structure as flat as possible. So if you follow my example of my previous comment, you would put your sharedMedia inside a separate reducer that will be at the top level. You don't need to have deep level of reducers unless you are describing the object itself: if you are referring to another object, just simply put the ID and select the information you need from the part of the state you need – Ematipico Jun 28 '17 at 15:34
  • 1
    Yes. I don't have any problem with the reducers. The article about normalizing reducer in the Redux docs does a pretty good job at expaining how to handle reducers without having to deal with deeply nested data structures. The only thing I was trying to do was to sorta find a way to document the initial state of my application. To have a picture of how my store is initialized. That's all :( – eddy Jun 28 '17 at 15:47