Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I specify the shape of the state in a React/Redux application?

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?

like image 872
eddy Avatar asked Jun 28 '17 03:06

eddy


2 Answers

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.

like image 119
markerikson Avatar answered Nov 15 '22 07:11

markerikson


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.
like image 43
Cory House Avatar answered Nov 15 '22 06:11

Cory House