Week 18: A Recap
Zen And The Art Of React Code Maintenance
Last week was a joy compared to the [insert a less offensive equivalent to the word ‘clusterfuck’ here] of the week before that. There were to major factors contributing to that:
- I did not need to be an absolute React professional to code up the kind of functionality that I needed for the route customization UI, and
- React is actually not that hard to learn properly if all you want to do is what was described in the first point.
As I wrote before, I decided on building a skeleton replica of my route customization feature with all the bells and whistles that a full-blown React environment brings to the table. And using
create-react-app for my test run proved to be the correct choice. The official React Developer Tools make inspecting component state really easy, saving me tons of time where I would have logged stuff to the console with my black box Preact package. Plus, with using proper React I was able to learn a bit more about best practices thanks to a couple of ESLint plugins that give you those wonderful nagging recommendations when something does not match the idiomatic React Hooks way. And, as a bonus surprise, React in development mode even provides some helpful error messages if it catches certain otherwise cryptic errors.
The caveats are immediately noticeable as well: My small test application code resided in a folder weighing over 250 MB. Almost all of it lives inside the dependency folder, though, and it includes cushy comforts such as a fully configured webpack, a dev server with hot-reloading and even luxuries like an included
lodash package. I was positively surprised that the optimized production build of the actual code was still a mere 40 (albeit gzipped) KB of JS and CSS. That’s not too bad.
My approach might have been pretty costly regarding the invested time, but considering that my main goal was to not get into the situation of resorting to hack and slash coding again, it was a good investment. Though it means that in the end I basically wrote the whole route customization feature three times almost from scratch: once in Preact (where I miserably failed), then in normal React, and then again in Preact. Although for the third variant I was able to carry over some React code, the barebones clone of all the UI in React (see image on the left) only worked with some local data and no validation checks. Still, it finally made clear to me how information has to flow in React and how valuable proper state management is.
One takeaway of my time spent with React was to rely on the
useReducer hook instead of the basic
useState. As soon as things get even a bit more complex, a lot of internal state will be dependent on each other and a simple UI interaction can trigger multiple state changes in different components.
useReducer allows handling those state changes with a pure function, a so-called dispatcher: this function receives the current state and an action (by convention some string like
'UPDATE_ALL_FIELDS' together with a JS object containing some new field data) and simply returns the new state based on whatever logic shall be executed for that action plus associated data. This dispatch function can even by used in conjunction with React Context, providing a global data store that every child component can access and safely mutate. What Redux was for old React (with all the added complexity and boilerplate code), you can now implement with just React Hooks in a way to satisfy most common use cases.
One small, persistent bug across all three iterations took my bone-headed self quite a while to figure out and it makes for a fun anecdote: when you activated a request method for a route by clicking the checkbox, another route that didn’t yet have a response for this method would also have an activated checkbox when you switched to it. It was an edge case in my manual testing runs, so I only ever started really investigating it when I noticed it again in my final iteration of the feature.
As it turns out, in that particular case I never gave too much thought to the implications of React’s way of figuring out if a rerender of the component is necessary. But in hindsight all alarm bells should have gone off in my head when writing those components for each request method: whenever a route does not have a JSON payload associated to a particular method like
PUT, the component receives just an
undefined primitive and has its checkbox turned off. When the checkbox is turned on but no data is entered into the associated
textarea, the internal state still holds the
undefined value. If you switch to another route, the component receives a new data prop, but it is again
undefined, making React think that the component does not need a rerender, preventing the checkbox from ever deriving a new state and leaving it exactly how it was in the last route.
It’s obvious, I agree, but for the best part of an hour, I was trying to hunt down some imaginary bug in the parent component. Imagine my face when I realized what was actually happening.
So, here it is now, the almost final version of the route customization, which took way longer than it had any right to.
What Is Left
I keep a hand-written list of things that still need fixing or improvements until I can call 200 OK done and start properly writing my case study (and, subsequently, my CV and then some job applications). And there is not much left on that list, at least when compared to the challenges of the past. I want to give the whole design a do-over, create a logo and a design a fancy landing page with some information, but this won’t take too long. The same goes for the documentation itself that will provide some much-needed information for users about what they can do with a 200 OK API and what the benefits of logging in via GitHub are.
There are also a few limitations I need to implement regarding the number of APIs that can be created (both for registered users as well as anonymous visitors). In addition to that, I need to write the small cronjob process that will periodically check for expired APIs and delete them from the database. That should be pretty trivial, though. And then it’s just fixing of whatever any testers can still identify as show-stopping bugs.
So one more week of recapping my work and I’ll probably be done and will start looking for an awesome job as a software engineer, right amidst one of the worst recessions and a global pandemic of yet unknown duration. What a scary time to change careers.