Personal
HOMEWORK ASSIGNMENT: WHAT I LEARNED
I just finished one of those take-home programming assignments that companies are sending out these days in lieu of whiteboarding exercises, a process that I'm genuinely thankful for. Whiteboarding is just a way to stress someone out; it doesn't prove anything (except excessive cleverness, sometimes). We work with a fully immersive IDE (even if Emacs is still making itself difficult in that regard), we work with StackOverflow available, and we work surrounded by books, notes, and cheatsheets. We even work with our fellow workers on the other end of Slack and Zoom.
A lot of what I did was "re-learning," but it's always good to go over it again, just in case. Here's a lot of what I absorbed:
CSS Flexbox
Although ultimately I threw my solution out, the first thing I finally knuckled down and actually studied, rather than cribbing from examples, was Flexbox. I now understand the basics of Flexbox: containers, wrap, direction, justification, and alignment.
The biggest lesson I learned is that flex-grow
doesn't work the way
you expect; it only dictates how containers should grow if they're
allowed, and after their initial space is allocated. You still need
to do things like set default widths, and you still need to reconsider
those widths in the face of media queries.
Despite how much I liked Flexbox, the homework's layout demands were beyond what I could achieve, and I eventually wimped out and used the EGJS InfiniteGrid instead. It's a lovely solution, and it's not React-limited, so I suspect I'll be reading the source code to see what lessons I can learn from it.
Javascript and React
I've been doing React since early 2015, when Splunk announced it was moving its stack from Backbone to React. This was before the introduction of React Hooks or Redux, and Splunk continued to use Backbone's "models & collections" for its data layer, which was a pretty solid decision given how battle-hardened that data layer already was.
I've never sat down and codified what I know about React, and more to the point, there are a lot of new and different aspects to React as it evolves.
The one thing that really upset me during the whole process was just how hard the Hooks API works to keep from having to redraw items. Since I was working in a search feature into an 'infinite scroll' view, I couldn't just reset the view every time; React didn't want me to do that, and starting with "no search" React would hold onto that state tenaciously.
I discovered that what I had to do was find a signal that the search term had changed and manually use a state setter to push the search term into the view. Since the view is a "Magic The Gathering"-like catalog, it eventually looked like this:
useEffect(() => {
if (cards.first !== rootUrl) {
setCards(emptyCards);
}
}, [cards.first, rootUrl, emptyCards]);
useEffect
runs on every re-render, and here, the state is recording
the 'seed' URL. If the two seed urls change, I have to use the state
setter to reset the cards block to 'empty'; this triggers the infinite
scroll feature to load as many cards as necessary to fill the viewport,
using the correct URL. This is a correct way to change the state on a
change to the props; it may not be the best way, but it worked for my
purposes.
I also discovered that React's useCallback()
is an excellent way of
debouncing an event loop that wants to hammer the back-end once too
often. By memoizing the "URL to use next" into the state object,
useCallback()
only loads data when really is a new URL to use next.
In that context, however, it's better to use the second-generation
Promise
API rather than the third-generation await / async
, as it
results in a cleaner, more readable code design.
Typescript
I continue to enjoy the hell out of Typescript. I know that the evidence suggests that strictly typed languages don't reduce errors, but by having that conversation with the compiler about types and correspondence, I was able to root out poor thinking in my initial designs, aver that my types lined up as expected, and produce a reasonable assertion that the schema I was pulling down from the source worked well.
On the other hand, I'm not so sure about deconstruction. The following are equivalent:
const newCards = [...cards.cards, ...data.cards];
const newCards = cards.cards.concat(data.cards);
The deconstructed one is fine, but it obscures the capabilities of the
Array
object in a way that annoys me; a user has to know two different
syntaxes for the same thing.
When I was working at Big Fish Games, one of my major roles was to pair
with junior developers as they were working and encourage them to look
deeper into the code, to look past React and see what was going on
under the covers. Many of them took React, Material and ES7 for
granted, and many of them came from a PHP or Java background. Teaching
them about CSS, how to use map()
/reduce()
/filter()
to eliminate
loops in their code, to think about an array or record as "just an
object in its own right" in important circumstances (Haskell's best
lesson, period), or to avoid an 'if' statement whenever possible, was a
huge deal. I know that lots of developers see deconstruction as a
benefit, and I can see the benefits, too. I just want to makes sure
that in the process developers also learn how to read the API manual and
use all the power at their fingertips.
As Captain Kirk once said, "You have to learn why things work on a starship."
Sass
I've been a LESS user for a long time, but I learned a lot of Sass working on this website, and I continued to learn it doing the homework.
Since I mostly work with designers rather than be one myself, I don't
have a good CSS structural idiom of my own. I'm working on that, but
for the most part I just have a root style.css
folder, a base/
folder with things like my fonts, colors, CSS reset, and default
typography, and then a parts/
folder where I describe the components.
Sass makes it super easy to import
all of these components. My
favorite trick is, in the font.scss
folder, to specify an array of
weights, filenames, and styles, and then to automagically generate all
of the font-faces I'm going to use with a single @each
loop. Then I
create a collection of macros (meta-programmming in CSS!) that map my
four basic font family-types (serif, sans-serif, code, and display) to
the macro names font-$familytype
, and now wherever you want one
of those, you just @mixin font-sans(1.6rem)
, and your fonts change
everywhere, magically.
The other thing I'm still trying to figure out is a good Sass naming
scheme for colors. Part of me wants to go with something boring, the
way you get $color-primary-0
and on and on. But that's hard to
remember, especially since I kinda like a little skeumorphism and end up
with some interesting opacity settings on my drop shadows. Still
working on that.
Conclusion
I'm going to be spending part of today repeating these lessons in a couple of different forms, so that I can isolate them. I'm becoming especially fond of the ideas, if not the code, behind DITA, and I want to figure out how to atomize what I've learned into easily digested, understood, and indexed lessons in my own idiosyncratic way, such that I can easily reload those lessons into my head whenever I need them.