The beauty of using a framework like React is the reusability of its components. This makes React a great choice when you need a UI that is capable of changing certain aspects dynamically, while still re-using components across the application… a few examples would be buttons, form elements, re-used layout components, and anything else you may want to re-use. This helps us as developers and designers begin to put a focus on re-usability and stop coding one-off one-purpose elements and/or functions. React helps us eliminate violations of the “Don’t Repeat Yourself” (DRY) principle when used correctly.
This probably doesn’t tell you anything you didn’t already know about React, however. With all of the power of re-usability and infinite dynamic nesting that React gives you at your fingertips, an interesting side-effect begins to appear: once your app grows past the humble stages of 4 – 5 pages, you will notice bugs crop up that just seem to stick around and become increasingly harder to conceptualize and fix.
Wait a second… wasn’t React supposed to simplify things through componentization so tracking down and fixing bugs would be easier? Like quicksand, the more you try to fix some issues, the more complex your UI logic becomes.
Take the example of a bubble notification component. Let’s say there is only one page that can create these bubble notifications. So, you just put the bubble notification at the parent container component and call a function passed down via props to update it. Easy enough… However, when you introduce multiple routes and even sub-components that need to also update that bubble indicator… well now you need some pretty hefty state management in that main component. To address this, we’ll need to have additional management for the bubble indicator route throughout the entire app. As you start to introduce additional updates to other components, such as the bubble indicator updating another component that could fire an event that in turn could update the bubble indicator – well, now you have a circular dependency which becomes even harder to deal with. This situation can be avoided with a single source of truth aka a data layer.
Enter Redux
Redux is a unidirectional state management machine. Redux was designed by developers who understood the single source of truth theory and decided that there is a large difference when dealing with structuring and updating data models as opposed to the components used in your user interface. Given the following pattern without Redux, you can see where you would need to structure your component in a certain way to allow your application to update all components with needed data.
In the diagram below, you can see that without Redux, your components would all rely on the data stored in the closest parent component in the hierarchy.
With Redux you’ll see something that looks more like the following diagram.
As you can see, each component can now update and consume data from one single source of truth (or store) that is reflected across the entire application as a whole. This pattern will help to greatly reduce duplication of data, reduce un-needed code, and ultimately increase coding efficiency while reducing bugs. When introducing things like routers, dynamic parent-child relationships, and complex data structures that your UI will derive from having a pattern in place that will govern the flow of your data, and in one direction, will also help you plan out the next logical step in your application’s evolution, while not “muddying the waters” if you will – with unknowns and complexity around your data structure / model.
Having a data layer will allow you to separate the data from components, making them less tightly coupled. This may seem like an insignificant change at first but once you decouple the data layer and make it available to the entire app, you start to realize better ways to create more reusable and declaratively driven components.
The top four benefits Redux brings to the relationship:
- Single source of truth – Storing your data all in one location eliminates the possibility of a duplicate value somewhere being forgotten.
- Less code, more value, fewer bugs – This is given by the architecture’s unidirectional data flow pattern and giving you a structure to update data from different components.
- Reduce race conditions – Race conditions can be hard to deal with. By storing your data all in one location, we can better address how the data is updated (from each component) and then address the actual problems of how the components should interact.
- Increase coding efficiency – Adds the ability to have re-usable data structures. This will help when your app grows larger than a few pages. Especially if you want multiple components to receive data from parts of your data structure without sacrificing development time or efficiency.
Till next time, this is Level Up Development saying stay ahead of the curve.