Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
Lately, Iāve been shocked by how many problems Reduxās selectors can solve. Thatās why Iāve become a big fan and Iām sad to see that pattern treated as an advanced one. They are easy to understand and, at the same time, enable low coupling / high cohesive code at the Reduxās stateĀ level.
I got to know selectors a couple of months ago. In fact, I started working on a crazy search page for a project: besides a normal text query, users can select a number of filters. Thing is, filters come in all sorts and shapes: some are a set of checkboxes, some are nested trees of checkboxes, some trees can be filtered with a text box. Not only that, they produce really complex queries with combinations of AND and OR. Not only that, they interact between each other. And the list of ānot only thatās goes muchĀ longer.
At the beginning I felt overwhelmed by all of that complexity. Eventually, I worked my way towards the light step by step. In this journey, selectors have saved my ass and become my bestĀ friend.
What follows is a story from the project on how I got to know selectors and why they are soĀ awesome.
Keeping stateĀ flat
At the beginning I had a store shape similar toĀ this
where locations couldĀ be
Does it look bad enough? Well, imagine the pain of doing an immutable update to check the node with id 3. In plain JavaScript that would look likeĀ this
Of course, I could use some library like dot-prop but that would be like sweeping the code smell under theĀ rug.
A better representation couldĀ be
Or the one I ended upĀ with
with the treesā hierarchies encoded in the paths (eg 1/2 is child of 1 and parent ofĀ 1/2/3).
With locationsByPath the update looks muchĀ nicer
Separating concerns
While working on the feature Iāve noticed something: locationsByPath knows too much. As a matter of fact, it has both domain knowledge (ie id, value and hierarchy) and UI data (ie checked).
Thatās wrong because most of the times domain and UI data have different needs and lifecycles. For example, in my case, once the filters are loaded in the store they are never updated. On the contrary, the checked property is toggled every time the checkboxās state changes. Therefore, I decided to separate theĀ two:
First of all this makes checking a node incredibly easy
Also, checkedLocationsPaths and locationsByPath are independent now. That means, they can follow different lifecycles. For example, checkedLocationsPaths can be present in the store before loading locationsByPath.
Unfortunately, refactoring to a flat state made my life easier with reducers but harder with components. In fact, the initial nested shape is much easier for components to dealĀ with:
Well, I could add some logic to the component to handle the flat state. But is it the right place for that? I donāt think so. In fact, doing so would mean coupling the component with the stateās shape. Also, I believe in really dumbā¢ components.
Selector to theĀ rescue
Reducers are in charge of writing the state to a specific shape. Therefore, itās a good idea to have something as close as possible to them to handle the reading part. Thatās where selectors come intoĀ play.
If you donāt want to take my word, listen to Dan Abramovās.
body[data-twttr-rendered="true"] {background-color: transparent;}.twitter-tweet {margin: auto !important;}
Colocating reducers with selectors turns out to be a much more important topic in my new course than I previously realized.
āāā@dan_abramov
function notifyResize(height) {height = height ? height : document.documentElement.offsetHeight; var resized = false; if (window.donkey && donkey.resize) {donkey.resize(height); resized = true;}if (parent && parent._resizeIframe) {var obj = {iframe: window.frameElement, height: height}; parent._resizeIframe(obj); resized = true;}if (window.location && window.location.hash === "#amp=1" && window.parent && window.parent.postMessage) {window.parent.postMessage({sentinel: "amp", type: "embed-size", height: height}, "*");}if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.resize) {window.webkit.messageHandlers.resize.postMessage(height); resized = true;}return resized;}twttr.events.bind('rendered', function (event) {notifyResize();}); twttr.events.bind('resize', function (event) {notifyResize();});if (parent && parent._resizeIframe) {var maxWidth = parseInt(window.frameElement.getAttribute("width")); if ( 500 < maxWidth) {window.frameElement.setAttribute("width", "500");}}
As a matter of fact, having the initialState, write side and read side in the same place is right onĀ spot:
Letās see how some the selector works with anĀ example
with nestedLocationsWithChecks(state) ready to be passed to the Component from the previousĀ section.
In other words, on one side the application works with flat state. Which is more convenient for data management. On the other side, the application works with nested data. Which is more convenient for theĀ UI.
More importantly, they can evolve independently since selectors work as an anti-corruption layer and prevent leaking structural coupling.
Bubbling up aĀ selector
In the previous section Iāve worked top to bottom by refactoring the state first and worked my way down to the component.
Letās see how the inverse looks like by refactoring some code Iāve written before getting to know selectors.
Thereās a page in the application where some articles are shown grouped by their category (ie articles belong to one category only). All the categories must be rendered always besides the last one which should be hidden ifĀ empty.
categoriesFrom performs the group by and translates the title for the current locale. Component renders each category separately.
Component is too smart and is coupled to the state shape (eg resources.cat1).
Letās bubble that logic up to a selector:
Ever heard about dumb components? With selectors you get dumber componentsā¢: they just destructure props and render them without any additional logic.
Outro
In general, the shape of the state is something only reducers should know about. The moment it leaks out of the store, code becomes structurally coupled.
Since reducers decide whatās the shape because they write it. Itās just common sense to make the āreadā happen close to them with selectors.
As a rule of thumb, doing state. is a mistake. The only place where that should be allowed is inside a selector. But only as long as the selector is colocated in the reducer which is responsible for that part of theĀ state.
Want to read more cool JavaScript stuff? Check out how functional programming can make your lifeĀ easier.
If you liked the post and want to help spread the word, please consider tweeting, clapping or sharing this. But only if you really liked it. Otherwise, please feel free to comment or tweet me with any suggestions or feedback.
Selectors in Redux are a MUST was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.