Learn to spot red flags in your React/JavaScript code š©
This opinionated article will explain some of the red flags to look for when reviewing a React/JavaScript project. Avoiding these patterns can make your code more performant, more reliable, and easier to maintain.
š© Look out for the let
keyword
Back in the ES5 days, var
was the only means at our disposal to create variables. ES6 introduced the block-scoped let
and const
keywords.
In my experience, I see very few situations where you should use let
. Sure, it has its place (like a counter, for example), but for most applications const
is better suited. Youāll soon see why.
Take the following common use case. It renders the amount
in red if negative, otherwise, it will be in black.
let color;if (amount < 0) {
color = 'red';
} else {
color = 'black';
}return (
<span style={{ color }}>
{formatCurrency(amount)}
</span>
);
The code is using a let
, but after initialization, the color
variable is never re-assigned. This is exactly the use case for a const
!
We canāt simply replace the let
with a const
because of how the code is structured. However, if we refactor to use a ternary, a const
works out perfectly.
const color = amount < 0 ? 'red' : 'black';
Not only did we go from 6 lines of code to 1, but by using a const
instead of a let
, our tooling will throw an error if we inadvertently reassign const
somewhere else in the code.
Here is the output from ESLint if I try to set color
to null
after itās defined.
/Users/donavon/Projects/my-project/src/index.jsx43:5 error 'color' is constant
So the next time you feel your muscle memory start to type let
, catch yourself and use a const
instead. Nine times out of ten, it will serve you just fine.
Benefits of using const
- Forces you to write cleaner code
- Compile-time checking of unintentional variable re-assignment
š© Destructuring is your friend
According to MDN:
The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables.
It also makes the code significantly easier to read. Take this snippet for example.
render() {
return(
<div className={this.props.className}>
{
this.props.isLoading
? 'Loading...'
: this.props.children
}
</div>
);
}
There are multiple this.props
operations going on. This is slower to execute (OK, marginally, but still, multiple object property lookups need to occur), and again, it adds visual clutter.
render() {
const { className, isLoading, children } = this.props; return(
<div className={className}>
{isLoading ? 'Loading...' : children}
</div>
);
}
By adding the single line of code, above, the rest of the code is more readable.
Benefits of destructuring
- Potentially faster execution
- Cleaner
- Less prone to hidden errors caused by typos
š© Spread over Object.assign
It used to be that making a shallow copy of an object or building an object out of other objects required Object.assign
. But today, with the help of babel, we can use the new ES spread syntax.
Hereās some code using Object.assign
.
const defaults = { foo: 'foo', bar: 'bar' };
const obj1 = Object.assign({}, defaults, { bar: 'baz' });
// {foo:'foo', bar:'baz'}
The code below outputs the same results, but using the object spread syntax.
const defaults = { foo: 'foo', bar: 'bar' };
const obj1 = { ...defaults, bar: 'baz' };
// {foo:'foo', bar:'baz'}
This āsyntactic sugarā allows you to see the data youāre working on without all of the noise and clutter caused by the ES5 plumbing. You can read more about noise and clutter in my article āNoise is all around usā.
Axel Rauschmayer has a great in-depth explanation of spread vs Object.assign
in his article āES2018: Rest/Spread Propertiesā. Itās well worth your time if you like to dig around in the plumbing.
Benefits of using spread
- Less clutter
- Potentially more efficient
š© Using a ternary instead of using a logical AND
For simple if
conditions, a ternary is not the right tool. I explain this in detail in my article about ternaries and logical AND in my article āConditional Rendering in React using Ternaries and Logical ANDā.
Benefits using logical AND
- Less clutter
š© Expression body arrow function
Arrow functions are perfect for writing Stateless Functional Components (SFCs) in React and come in two forms. The statement body form like this.
const SomeFunction = () => {
return 'value';
};
And the expression body form that has an implied return
statement.
const SomeFunction = () => 'value';
Some people erroneously call this āsingle lineā, but as you can see below, the expression can span multiple lines. Note that Iām using parentheses and not curly braces.
const SomeFunction = () => (
'value'
);
So if your function returns a single expression, and you donāt need any intermediate computational const
statements, use the expression form on the arrow function. Itās a simple idea, and almost too obvious when itās written out like this, but itās an easy thing for some people to overlook.
Fortunately, ESLint again can come to your rescue.
/Users/donavon/Projects/my-project/src/index.jsx 12:17 error Unexpected block statement surrounding arrow body;
move the returned value immediately after the `=>`
The only thing to remember is that in order to return an object in the expression form, you must enclose the object literal in parentheses, like so.
const SomeFunction = () => ({
foo: 'foo',
bar: 'bar',
});
Benefits of expression body arrow functions
- Less clutter
š© DRY up that duplicate code
You didnāt think I would write an article about red flags in your code without mentioning DRY, did you? Here we goā¦
Take a look at the following two SFCs.
const Foo = () => (
<div>
<h2 className="sectionTitle">
Foo Title
</h2>
...
</div>
);const Bar = () => (
<div>
<h2 className="sectionTitle">
Bar Title
</h2>
...
</div>
);
Notice how the highlighted sections of code above in Foo
and Bar
are nearly identical. Itās apparent that they both display a title in a certain style. What if you had the same code in 4, 5, or more places? Itās highly likely that any changes done in the future would require that you make the change in multiple places.
You should refactor the duplicate code into its own function. This is called DRYing or Donāt Repeat Yourself.
const Title = text => (
<h2 className="sectionTitle">
{text}
</h2>
);const Foo = () => (
<div>
<Title text="Foo Title" />
...
</div>
);const Bar = () => (
<div>
<Title text="Bar Title" />
...
</div>
);
Benefits of DRY code
- Cleaner
- More maintainable
š© Why are you using a constructor?
Many times, when writing a stateful React class component, you create a constructor to set the initial value of state
. Here is a common example.
constructor(props) {
super(props);
this.state = { count: 0 };
}
But did you know that you can do all of this using the new class properties proposal? The code above can be written simply as this.
state = { count: 0 };
You can read more about this in my article āThe constructor is dead, long live the constructorā.
Benefits of not using a constructor
- Cleaner
š Conclusion
As I said in the opening, learning to avoid some of these patterns can make your code more performant, more reliable, and easier to maintain.
These are not hard and fast rules, but general guidelines meant to open your eyes to other ways of looking at code. Use with caution. Your mileage may vary.
Strict ESLint rules will go along way to helping you spot some of these for you, orā¦ you can always tag me in your pull request. š
Iād like to apologize if this article seemed like a flashback episode of your favorite sitcom (I hate those), but I wanted to expose those of you who have never read my other articles, with the chance to dive deeper.
I also write for the American Express Engineering Blog. Check out my other works and the works of my talented co-workers at AmericanExpress.io. You can also follow me on Twitter.