Redux Entity Classes: An Alternative to Selectors
By Traction
Redux is an awesome tool to handle the state of your apps.
Used correctly, it can help you add features to your development process that can save you many headaches. However, Redux by itself won't solve all of your problems. You still have to build your app, design the state structure, dispatch actions, handle state updates, query data from the state, etc.
One of the most interesting parts is querying data from the state. I'd like to be able to get my data and use it everywhere, regardless of whether I'm using a framework like React, Vue.js or just plain vanilla JS. The suggested way to handle these queries is using selectors that take the current state as the first parameter and use it to reduce or filter data.
In this example, we are using a simple function to query the required data: it will find the Brand and return it as an object.
Selectors work as independent, predictable, and testable functions that can be used within other selectors to create more complex queries. So, if you want to get the Brand of a Device you can nest functions to get the required data.
An alternative approach using entities
In this exercise, we will use the following data as our state:
Following the selectors idea, we can use a more object-oriented approach using the ES6 class syntax and static functions.
The basic concept is to create entity classes with static functions in them that handle queries related to that specific entity. This lets us create entities to suit our query needs and prepare data that's easy for an app to display and consume.
So, what's the difference?
First, we're not passing in the state of the app as a parameter, which I talk about later on. Second, these are static functions inside a class. And the third and most useful difference is that instead of only returning a simple object or array of objects, these functions return an instance or array of instances of that class when possible. This allows us to design our entity to contain a lot of functionality.
Returning an instance
When your static function returns an instance of a class, you get the advantages that a class provides. For example, you can use a constructor to build an object, have getters and setters for specific properties, and even incorporate complex functions.
The following is an example of how the Device class could look with its respective constructor:
In this example, the first thing we do is define the Brand class. The constructor for this class takes an object with the Brand parameters from our state, which can then be transformed or assigned to the instance. Next, a setter transforms data and assigns it to the class instance. Finally, a static function takes in the state and the ID of the Brand, queries data from the state and returns a new instance of the same Class.
Data association
Data association is useful when you work on relational entities that have many links between them. Because we can set whatever we want inside the constructor, we can associate necessary data at the same time. Let's make a class called Device, where each device has an associated brand in the brandId property:
To do this, we need to associate the brand with the device on class instantiation.
Updated state for everyone
Depending on your app dev environment and configuration, there are many ways to make the updated state for your classes available to your app. The recommended way is to subscribe to the store and get the new state every time an action is triggered, then pass in the state and make it available for all the JS that needs it. But for this exercise, we'll pretend we are working on a modular app and make the state available in a Class that has a static variable that always updates using a middleware function.
Think of this middleware as the bridge between the triggered action and the data storage: We can intercept this process and, before storing the data, make this new store data available in a variable. The data can now be used everywhere and allows our class to be a bit cleaner while still dependant on the app state.
JSBin example — with the updated state as a variable.
JSBIN example 2 — with the updated state available in a class with a static variable
Conclusion
This approach allows us to create entities in a way that suits our query needs. It's very useful to have an entity that represents our data — and prepares it in a way that our app can easily display and utilize. Imagine the possibilities of using this approach in a larger application. Entities can be linked to each other and provide the relational data in one Object, which then has the ability to create more complex queries just as you would with selectors.
Your imagination is the only limitation.
Some people call me a "candy ass." And I'm okay with that.
The future of graphic design is immersion.
Jenn Maer of IDEO talks about her background as a story/copywriter, IDEO's "design thinking" approach, and the big "oh shit" questions that they strive to answer.