Technically Correct

I learned a lot from my first time working with Redux and React on top of Rails. Hopefully, something here can help another beginner down the line.

Understanding State (and thus, Redux)

I didn’t really understand Redux at all when I started the final project. Everyone often refers to “Rails Magic” and how complex things are obfuscated behind relatively simple functionality. Compared to Rails, Redux seemed like pure, unadulterated witchcraft.

So, you’re telling me that I just type a bunch of random functions at the bottom of a component, and then the data just arrives there?

Me, reading the lessons for Redux.

I looked at dispatch, mapStateToProps and mapDispatchToProps and really had no idea what I was actually looking at. What even is state? And what’s a store? Where am I telling my program to look when I type that? Why do I need to map a function to the props of a component? Why can’t I just import it like I do all the others? Do I have to do this everywhere?

So I did what most people do when confronted with a new way of doing something — I completely ignored it and used what I already knew.I wrote Redux into my Root component, and then I passed every single solitary piece of data from Root’s state to a component, repeating that process all the way down their respective component trees.

Prop declarations were stretching far off the page, in some cases breaking Atom’s color schemes all together. I was copying and pasting more than I was typing. Half of my code was just me passing the other component’s props to the next component. And then, somewhere down the line the data would get missent, so I would have to trace the components, and their individual states, in the React devtools until I found where I had messed up in the code, and adjust it. Finally, it reached a point where I realized, “This ain’t right, man.”

I took a break, went to Google, and typed in “Redux tutorials.” Despite the fact that I had already spent a good chunk of time on very in depth tutorials and amazing coaches from Flatiron, something wasn’t clicking. I stumbled upon two different resources that helped me digest Redux better than ever:

  1. Code Cartoons – “A Cartoon Intro to Redux”
  2. Dan Abramov’s Egghead.io Course

The Code Cartoons explains the purpose and the use-case behind Redux stores, actions, reducers, etc. by anthropomorphizing them into a bureaucracy that, as a Political Science student and current office worker, made far too much sense. On top of that, it didn’t use a single line of code to try to explain itself.

Dan’s course, on the other hand, is the essential Redux 101. It’s comprised of no nonsense, stripped down, fast lessons that have you build upon a single JS file, without bogging you down too far into the minutiae. It helps that he wrote the damn library, too.

They both highlight an important method to understanding the library: you can’t learn it all at once. You need to know how the pieces connect before you can see how the solution it offers actually helps you.

I was able to dumb down Redux into the following, in my head. It’s by no means exhaustive, but this is how I dealt with it.

  • The store has a current state of data
  • The UI displays data from the state.
  • The UI also has functionalities within it.
  • The user uses a functionality
  • The functionality tells the reducer a certain action has taken place, and what is changing.
  • The reducer looks at the state, modifies it (sorta, not really) according to the action’s wishes, and then sends a new state to the store.
  • The store then tells the UI, “Hey, we got a new state over here!” (this is connect)
  • The UI displays data from the state
  • ad infinitum

The “a-ha!” moment came for me when I realized that I was in far more control of the data with a store and a state than I had realized. When you make a reducer, you’re also essentially making a “state” for that type of data, for lack of a better word. Navigating and using the data in the state became a matter of mapping data correctly with connect and using it quickly instead of remembering where it is and where it is coming from at any given moment.

Schema and Crafting Endpoints

Equally important to viewing and modifying data is storing and retrieving it.

That magical “boilerplate” in the reducers that sets the state allows you to control the shape of your data. You don’t have to make anything more complicated than you want it to be. The data can be broken down as small as you want, or as large as you want, so long as you understand the format your API is sending it and how your actions handle it.

The way actions work on Redux make it imperative that you know exactly how the data is coming in and how it is being processed throughout the reducer. Any mismatch in formatting or structure can grind React pre-production development to a halt, especially if you don’t have a contingent componentDidCatch() in your React code. Also, without proper testing, you’ll likely end up tracing where your data didn’t enter the state using Redux, React, and debugger with even Vanilla Chrome DevTools. There’s nothing wrong with F5 debugging, but keeping structure consistent will save you from headaches.

Additionally, Rails is really powerful and flexible. The way it uses and builds upon the user-friendliness and benefits of Ruby allows for a legible and straightforward experience with manipulating response daata. Do you want to include an object list that has a belongs_to relationship with the data you’re seeking? That syntax within a controller is literally Object.includes(:relative).

Lastly, and this ties into the next point, make your API endpoints conventional and straightforward. It was my preference to err on the side of more routes and more specialized endpoints for specific use cases where I could get exactly the data I need rather than trying to combine or reuse other calls. This allows you to make less API calls in the long run and reduces the error potential in handling data, as no further manipulation or organization is required.

Naming Conventions and Separation of Concerns

This is a quick, but important point. There’s absolutely no benefit in being cute with Action, Component, or State/Prop names. And don’t make anything do more

Here’s my personal style guide. For Actions, prefix USE_ when you’re replacing a set of data, ADD_ when you’re adding to the state, REMOVE_ for removing something from the state. Have the suffix match the name of the state property you are “modifying.” This may seem very simple, but let me outline a problem I had that outlines the importance of this.

In my application, the user can keep track of Phish concerts that they have seen. The main UI method for doing this is having them select a tour, see the shows from a specific tour and toggling a button that Adds/Removes the show from the User’s profile. It’s pretty straightforward behavior, and nothing revolutionary. The button was designed to toggle between two states: The first (“Add Show”) if the show was not within a User’s list of shows and another (“Remove Show”) if it was. I built the prototype of a user’s profile using a date picker which added a show if there was a show on a specific date.

The reducer action was titled ADD_SHOW, which added a singular show to the shows state and to the user’s user.shows entry in the database, thus displaying an additional show in a list of shows on the user’s profile. For the profile was a perfectly acceptable behavior. However, for the button, I did want to add the show to the user’s profile, but not to the overall shows state, as that would add a show to the list that was already there. So I had to decouple actions, one ADD_SHOW, which remained the state/list modification action and another ADD_USER_SHOW which added the show to the user’s database entry.

I already had the logical work done, in both scenarios, but the application’s set up requires those two to happen separately. And by sticking to a convention and simple action names, you can gleam very quickly what it’s supposed to do without needing to scour code.


Posted

in

by

Tags: