Saturday 28 December 2019

React - A Follow Up

Note: This is a follow-up article to my first experiences working with react.

Shopify is full of many intelligent and skilled developers and, now that I work with them, I've gained the benefit of the experience of those much more experienced with React. We maintain are own React component library called Polaris but, that said, many of us are still novices at writing React apps. Now that I've gained more experience, I have more thoughts to share!

1. App Structure

I thought I'd gotten better at this but many screw ups later I've realized I still have much to learn. Having a src/ directory is standard for React apps but it isn't always clear how to organize the sub-directories. There's also a lesson in writing components that I had to learn.

A components/ directory is create and you can nest components in whatever way works best for you, provided there's sound logic to it, but knowing how to write those components can be pretty confusing for the novice React developer. The key is understanding how to utilize dumb, generic components alongside domain specific components.

A single page application can usually be broken down into separate pages and it makes sense to have a pages/ directory. These pages are typically pretty domain specific and might contain sub-directories for components that only ever relate to that particular page.

Another option might be to create domain specific directories. A blog app could potentially organize different views into directories, like posts/, comments/ and users/.

Continuing the blog example, since you might have a list of comments, a list of users or a list of posts, it stands to reason you should only have one component to display them, wrapped by a domain specific version if necessary. Creating List and ListItem components that are wrapped by CommentList and Comment components respectively, lets you optimize your component usage. It stands to reason that, for the most part, your lists will have some common properties.

2. Per-Component CSS

So, now we have CSS-modules. A blessing and a curse. On one hand, it removes the problem of namespace collisions and unwanted style-sheet cascading issues. It makes your CSS much simpler and removes much of the necessity of depending on SASS or LESS. On the other, it's more of a pain in the ass when you want to override a style. Good structure and organization of your CSS and where you apply certain styles is key.

Overall, I'd call them a net gain but not without their own warts. Regardless, I still highly recommend per-component CSS!

3. Network Communication

As nice as promises and the Fetch API are, I have to say I now prefer Axios. "Fetching" a POST just feels odd. Not being able to catch non-network errors (40x responses) within Promises feels non-intuitive; it requiring extra boilerplate.

Axios just feels more natural. The verbs for performing actions make sense. 40x responses can be caught instead of being checked for. Browser compatibility, if that's a concern, is covered.

And now...


That's about it! Nothing too revolutionary but still, some nice tidy lessons from being in the trenches.

Ta-ta for now!

Tuesday 30 July 2019

My First React App And What I Did Wrong

I recently had the pleasure of using React to build an internal use only app for Acro Media. I say pleasure because it genuinely was despite the fact I was met with many frustrations with using React for the first time. To be fair, these were more my own shortcomings trying to learn a framework I'd never used before, rather than the framework itself.

As with building any app, my failures taught me far more than my successes. Here are those failures:

1. Poor Structure

This stems mainly from the fact I didn't understand what React really was. I was aware, of course, that parts of my page should be abstracted into components but I didn't understand how to truly utilize that. I didn't, for example, build my components in a way that would make them reusable in a concrete way. Not only were they not reusable within the app but they couldn't be copied into another app, either.

I didn't understand that even though I created a table component, with header and body sub-components abstracted out, I made them very specific to the app. While this app only needed the one table, and so this worked out fine in this instance, I would have had to do a lot of refactoring when I had the need to add a second one. Instead, I should have created a generic table component and used props or higher order components to customize it.

Finally, I used a single src/ directory to hold my handful of components. Even though this worked fine in this very specific case, if I'd laid out the project, and thereby the components in a more discrete way, I would have required a better defined structure.

Generally speaking, you'd have a Components/ directory separated into components, like Table/.

2. Poor Per-Component CSS

When I began the project, I was aware you could import CSS into a React component. I assumed, wrongly, this was just a nice way to abstract CSS into smaller chunks. While this is indeed a benefit, how wrong was I?

Thankfully, before the project's end, I realized the power of using per-component CSS. I was further relieved to discover how I could leverage SASS and Webpack with React to make a very powerful combination of tools.

3. Poor Understanding of State

This was highly frustrating. I had only two main components: a form in which you could set parameters with a button to submit them and a table where fetched data should be displayed. A classic "report" app. The problem was, these components were sister components, on the same level, with a common parent but I didn't understand how to react (lul) to an event in one component so that the other would automatically get updated.

Searches repeated suggested using Redux but I was convinced that route was completely overkill for what I was trying to achieve. I wasted a heck of a lot of time looking into Redux and other solutions. Shouldn't it be simple to link components up? Of course, I could use props from the parent to provide callbacks to update state in the App but that felt so wrong.

How can something that feels so wrong be so right?

Maybe there is an even better way but as it turns out, using Hooks (or state in a class) is really the easiest way. Store the state of the app in the top level component, pass a state update function into the form component and pass the state of the app as a props to the table component. Easy-peasy.

Another instance might be to create a global "state" class and you could dispatch updates from components but, honestly, you may as well use Redux at that point.

4. Lots of Classes

The documentation confused me. If I used a function to create a stateless component, and I used a class to create a stateful component, why not just make every component a class? Why did React prefer or encourage functions over classes?

Well, in 16.8 at least, you almost never need classes any more and using functions is actually a lot more straight forward, requiring fair less boilerplate. After many hours of use, I came to rewrite all my class based components to functions and I couldn't be happier. Viva function components!

5. No TypeScript

Not everyone may be a lover of TypeScript, and I'm certainly not an evangelist either, but I also appreciate what it's trying to do. I am a fan of strong, static typing and TypeScript is a good step in this direction.

When I started the app, I had some logic only portions written in TypeScript. Unfortunately, as development ground on, I ended up converting this code to pure Javascript because of a frustrating bug. I had concluded, wrongly, that something was being happening in the transpiling that was causing my issue.

As it turned out, this was not the case and I regret that I never converted the code back to TypeScript. Whoops.

6. No Object Destructering

Prior to this project, I wasn't familiar with destructuring in Javascript. After learning about it, I used array destructuring any time I used a React state Hook but, for some reason, I still didn't know about object destructuring. This in spite of the fact I came across examples that used it. I just didn't comprehend what I was seeing.

Now that I know about it, I wish I'd used it. Ah, well...

Things That Went Right

Here is a list of things, without explanation, that went right:
  1. Yarn dependency management.
  2. SASS.
  3. Fetch API and Promises.
  4. Webpack
That's all the wisdom I shall pass on.

Adieu!

Saturday 6 April 2019

Trying Ruby

I've never used Ruby. I have friends who swear by it and the Rails framework is incredibly well known. All I know about the language is that it's dynamically typed and incredibly flexible. A million ways to do any given task.

Now, I am not typically a fan of dynamically typed languages. I use PHP in my current job and while its "typeless-ness" can be useful and allow for quick prototyping of ideas, it is also fraught with many pitfalls that bite you in mysterious ways.

That being said, you can program in PHP, and I suspect Ruby, in a safe and responsible manner and I find myself interested to see what I can do.

Installation

To get started, I've gone to Ruby's main website. I normally work in Ubuntu but today I'm on my Windows machine. A quick skim over the page and that leads me to the Windows Ruby Installer page. I chose the default of installing Ruby with the devkit, which includes MSYS2 so gems that require C can be compiled. It seemed like a reasonable choice since I was new to the language.

After running the installer, I was given the choice to run an MSYS2 utility for further installation configuration. I chose the defaults for all options.

I also decided I wanted to install Ruby on Rails. The tutorial page for the framework warns you should get to know Ruby first, and I probably should, but I'm going to go ahead and install the framework anyway. I am already aware of the gems package manager for Ruby and used it to install Rails per the Getting Started document on the website. It also looks like using the Windows installer was a good choice as it includes sqlite3 by default which the Rails tutorial recommends. Excellent!

Getting Started

These tutorials can be a mixed bag. With programming languages, they tend to be slow, boring and uninformative; targeted at new programmers and not experienced ones. That said, I've opted to give the Ruby quick-start Ruby in Twenty Minutes a whirl to see what's up.

It encouraged me to start up the REPL for Ruby and do some basic tasks. Some notes:
  • There's an exponential operator (**).
  • Variables are dynamically declared, as expected, and require no special characters to denote a variable.
  • Function declaration are reminiscent of Pascal where you declare the function start (def) and close the block with end. An interesting choice.
  • Functions can be called with or without brackets. I'm not a fan of that and I think in my own code I would always include the brackets.
  • String interpolation is nice if not slightly unconventional compared to other languages with the feature.
  • I was glad to see there's some kind of notion of private/hidden class fields/properties.
  • The ease of use of reflection is interesting.
  • Accounting for variables types will likely require a lot of boiler plate. 
  • Using `if __FILE__ == $0` reminds me of Python.

Initial Musings

There are certainly things I like and things I don't. I prefer statically typed and strongly typed languages. Ruby is dynamically typed and weakly typed. Like with PHP, it's just something you have to live with or work around.

It might be a bit premature to say after only spending a few minutes with the language, but it seems that like with most dynamically typed and weakly typed language you have to expend a fair amount of effort with boilerplate to determine what type any given variable is to take action on it. That said, the duck typing and using reflection does cut down on this a bit.

Coming bundled with an industry-wide accepted package manager is a refreshing change of pace. Package management is a tough topic and many languages still struggle with it. NodeJS, for example, has multiple competing tools. Go is only just starting to figure out its own management system. C and C++ don't really have one unless you could system tools like yum or apt.

Honestly, this is my second look at Ruby. In my first attempt, many years ago, I can quite confidently say I did not give it a fair chance and I didn't like. That's not Ruby's fault but my own. This second go around has left me with a different impression and I look forward to delving in further.

Going off the Rails

Where's the Rails portion I talked about? Well, that will come later.

Ta-ta for now!