Publishing My First Enterprise React Project — The Takeaway
As a self taught developer (shout out to freecodecamp.org), working on the largest React project I've ever seen and then publishing it felt a lot like taking a trip to a foreign country. A place where very few people speak your language. In these situations you have intention, and some idea of where you want to go. But you’re limited in many ways, and at the whim of whoever you have available to point you in the right direction. There are things that made it easier to move forward, and a few things that make the resulting product excellent. Following declarative patterns, maintaining a flat(ish) and clean state object, and working with promises helped make this a successful release.
To provide a little bit of context, I've been working on the latest release on Zesty.io - our new accounts experience. We've been actively rebuilding the interface from the ground up in order to provide loads of new features, more intuitive design, and more efficiencies throughout the new interface. This article focuses on the experience of building the new interface in React.
Let the Tool Do The Work
React as it’s designed is declarative, as is React Router. Redux follows a functional pattern which fits in nicely. In any skillset, a real breakthrough happens when you learn to ‘let the tool do the work’. Imagine shaving and trying to press the razor through each individual hair. Do you want to cut yourself? Because thats how you cut yourself! Let the razor do what it does; when you use it at the correct angle, it just glides.
This applies to programming. Many programmers (myself included) have a tendency to try to write fancy (unreadable, unmaintainable) solutions. While this may not result in immediate and profuse bleeding, it does result in frustration when co-workers have to work harder to understand the solution than the problem it was meant to solve. Enter declarative patterns: if I want a view, I write a route, and the route gives me clues on what the view will contain.
One example of this is in our instances sub-app. The instance grid view is on the /instances route. This view contains, you guessed it, instances. Each Instance is (initially) in a grid, in a card (yup, a card component). If you want to see an instance in more detail the overview is rendered when a unique ID appears, eg. /instances/{unique-id}. Now our overview component is rendered and fed that ID, progressively feeding data down to the components that give the user the information they are asking for. This is completely straightforward, but the approach is a constant reminder that these tools are meant to be used in a particular way. I found myself in the React and React Router docs often, and each example steers you in a direction of writing code that is demystifying, I wont say self-documenting.
Too Much Boiler-Plate
Redux is portrayed by some in the community as a nightmare to work with, ‘too much boiler-plate’ and not enough utility. If you agree with this sentiment then you likely have not run into the problem that this tool was meant to solve. An app of this size all but requires the use of a global state solution.
The problem with redux in React is that React provides enough tools out of the box to satisfy most data flow requirements. Now with the context API it’s even more capable of handling these issues, making redux and the like more niche. That said, this became a huge learning tool for me. I love the functional style, the middleware options, the abstraction itself allows me to focus solely on dealing with the data layer of the app and nothing else. Our data is heavily nested, and as our backend strictly follows REST conventions each resource requires additional fetch calls. Map, filter, and reduce have become close friends of mine. Ive also made some very influential acquaintances in sort, some, and find. Not to mention Object.keys which at this point is essentially family. The result of all of this is that our components get to consume some props and display useful, consistent data to our users. However all of these data transfers happen asynchronously, so the solution to this for us is promises.
A Sink, A Weight
Not async await, that (at this point) isn't a robust enough solution for our use because we need error handling and throwing everything into a try catch just isn't an option (and yuck?). Promises opened the door to handle a few problems, one is the number of requests we have to make, the other is dealing with the data as it comes back and having the freedom to make it dance if we want to. I found the .then and .catch interface very intuitive. Promise.all also makes an appearance or two in this app, which is really cool to be able to just do all the things, and then do something in response to those promises resolving.
I realize as I'm writing this I have just described some of the approaches that we use and how they work in a broad way. This is supposed to be what I learned, and well, what I learned is that there is a huge difference between ‘knowing’ and ‘understanding’. I have a much better understanding now of React and adopting flexible and scalable component structures. My fear of redux has been extinguished and replaced with adoration of it’s simplicity. And I am now grounded by the declarative power of React Router. I can't wait to have another epiphany as the result of pounding my head against broken code and re-reading documentation. It's so simple.