6

How (not) to Write Readable JavaScript and React code

 1 year ago
source link: https://blog.bitsrc.io/how-not-to-write-readable-javascript-and-react-code-7bf56322e02d
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

How (not) to Write Readable JavaScript and React code

Tips for writing understandable code

Have you ever come across a piece of code and asked yourself; is this incredibly smart, and I don’t understand this, or is it bad code? I recently had to work on a repo and came across the little gem below. Please take a moment to look at the code and try to figure out what it does.

1*sKLw9AHP9EYifC8ra67OYw.png

It might pick some label type depending on the context, which could include language, label value and whether or not user-data is supplied.

I didn’t try to decode any further to find out what was going on. Let me know in the comments what you think it does.

Even if we don’t fully understand the function of the code, we can smell that there is something wrong with it. It is doing too much and, at the same time, too little.

Context

This code was in a folder called helpers, and the file was called labelPicker.tsx It was used in a React component called <Label>. The label-component returns either one of 2 different concrete label components, let’s say <ButtonLabel> and <FormLabel>, and in some other cases, it will return a string that is changed from “name” to “name’s widget”, and when no other case matches it will just return the string that was supplied. Hmm ok, are you still with me?

Figuring out what the code means

Photo by Ben White on Unsplash

It is easy to say that we do not like this code, but why don’t we like it? Why isn’t this good code, even when the original developer probably thought it pretty clever? Can we identify standards to adhere to so our code makes sense?

Descriptive names

What does the function name “pickLabel” tell us? What do we expect it to return? Maybe it returns a label from a list? Does it return a <Label> component or a label string? Would formatLabel be a better name? It is important that we name and organise our code so that we can easily understand what it does.
Let’s not forget magic numbers. Constants and variables should be named correctly. For booleans always use the positive as a name, so isValid instead of isInvalid.

There are only two hard things in Computer Science: cache invalidation and naming things. — Phil Karlton

Readable code

Most people aren’t familiar with multi-line ternary statements. If a developer knows how to use them, then it is still challenging to understand the exact return type of this code. It would be better to have an explicit if/else or a switch statement. Or maybe it is better to create multiple lightweight components for each use case we need to cover. But whatever architecture we use, the intent of the code should be obvious at first glance.

0*MKftLBAYco3ibj6Y

Photo by Sangga Rima Roman Selia on Unsplash

Keep it simple

In this article, it would be possible for me to write the most elaborate and eloquent sentences, showing you, the reader, how smart I am and what exquisite words I know how to use.

But let’s be honest. Simple language with shorter sentences is often much easier to read and understand. I want to convey my message, not show off my grasp of the language. In our code it is often the same. Keep it simple and explicit. Don’t be afraid to use an extra line of code if it makes things simpler.

1*vVH_0ITuANppD6hck8nwTA.png

Dall-E’s interpretation of confusion in the style of M.C. Escher

Avoid helper functions

Helper functions are an anti-pattern that I see too often. They are created when a file becomes too large in the opinion of the developer. The fix is then to just move some of the code to a folder called helpers, utils, lib or something comparable. Yes indeed, a folder called helpers rarely helps anyone. We should organise our code in accordance to the functionality of the code and the design pattern we use. React for example has components organised in a tree view of concrete components, we should try to stick to that paradigm, and then we can have services, hooks and stores. When we follow this pattern a helper file is rarely needed.

When components do too much or require too many props, it is that these helper functions often pop up. It is best to stick to smaller concrete components in a composable architecture. Create simpler methods and not just remove code to put it in a separate helper file.

Simple method parameters

For our code to be composable, methods should have a simple interface and be easy to use. Just like the everyday objects that we use, we expect functions to be ergonomic and a pleasure to use. The best APIs we like to use are inherently simple, like fetch(‘http://example.com’).

The complexity of the interface should not exceed the complexity of the function body. In our example above, the function body actually doesn’t do much, but it requires a lot of parameters. This will make it non-reusable.

Like our everyday objects, we hide complexity behind simple interfaces. We do not know how the internals of a car work, but we can drive cars as different as a Toyota or a Porsche without having to re-learn how to drive. They share the same simple interface.

Concrete code instead of auto-magic functions

It is a nice idea; we create our application from some config and use helper functions to process parameters for generic components, like a label that displays in specific ways depending on some context. By trying to create code that can be used for a variety of use cases by adding a lot of options and parameters, we often miss the most common use case or make it hard for the developer to use our library. When we encounter a bug in an automagic type of library it is extra hard to figure out where things went wrong.

1*hWihv3XyWE1jOYL30NMSzg.png

Confusion by Dall-e

Coupling

Developers can go long ways to avoid tight coupling between components, and yes indeed, tight coupling is bad, but think about the opposite end of the spectrum, lack of cohesion. When we have lack of cohesion it becomes impossible to navigate the code or understand how one part gets instantiated or relates to another. Such applications become as unmaintainable as tightly coupled applications. This is a balancing act that can be difficult to get right. Redux brought loose coupling but also a lack of cohesion, it is often not intuitive which event to send over or the precise flow of a click to an async call and a state update. Have a look at my article “Use Observables to Render React Components” for some examples.

The solution in React is to scaffold your applications with a clear separation of concerns, using a view layer, hooks as controllers and services. Write concrete code that is simple to use and put your React tree together. Use hooks to communicate with services; that is all there is to it in most cases.

Concluding

Back to our example, the original developers clearly had some good ideas, they wanted to keep the code DRY (Don’t Repeat Yourself), compact, and they wanted to render some components based on configuration.

Keep code concrete and avoid helper functions. Try not to be too smart and re-invent the wheel or create code that can fulfil every use case.

Write code that can be scaffolded together to add functionality.

Prevent spaghetti code by de-coupling, but make sure to maintain cohesion.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK