DEV Community

Cover image for React Clean Code - Simple ways to write better and cleaner code

React Clean Code - Simple ways to write better and cleaner code

Tyler Hawkins on February 22, 2021

Clean code is more than just working code. Clean code is easy to read, simple to understand, and neatly organized. In this article we’ll look at ei...
Collapse
 
mrvaa5eiym profile image
mrVAa5eiym • Edited

"Use the && operator instead." not sure about this. see kentcdodds.com/blog/use-ternaries-...

Collapse
 
drarig29 profile image
Corentin Girard

The operator && is not the problem here. In the example of the article you linked, he's using Array.length as the left operand of the operator. But obviously the 0 is falsy and you return 0, so a zero is shown.

You could just do myArray.length > 0 && <yourJsx>. Which is more readable.

Always pay attention when you are using the truthiness of an Integer alone.

It's just the same as if you wanted to conditionaly do something related to a score for example :

const score: number | undefined = 0;

if (score) {
  // do something with the score "because it's defined"
}
Enter fullscreen mode Exit fullscreen mode

This code is incorrect and should check score !== undefined instead.

Collapse
 
thawkin3 profile image
Tyler Hawkins

Agreed! Kent points this out in his article too that his downfall was using contacts.length in his code rather than contacts.length > 0 or !!contacts.length or Boolean(contacts.length), not necessarily the usage of the && operator.

Thread Thread
 
mrvaa5eiym profile image
mrVAa5eiym

agree, but I think it is not worth to have think every time how and why you use one pattern or the other. I prefer to reduce the amount of things I need to keep in mind (given they are already countless) and stick with one pattern that works for all

Thread Thread
 
drarig29 profile image
Corentin Girard • Edited

Yeah but all the gotchas the article is speaking about are misuses of the operator &&. If you really know the operator and just don't throw it everywhere without thinking one second (which you should always do, like in any language, especially in JavaScript), then you'll never have any problem.

Plus this shortcut is really readable. Most of the time, you don't ask yourself if it works.

For the length thing, I think you should basically never use the shortcut where you simply test its truthiness to know if the array is empty or not.

And for the error about returning undefined with { error } as a function argument, you just have to know that object destructuring can return undefined.

Anyway, do what you want, but I always use it without any issue.

Thread Thread
 
adamashored profile image
adam-ashored

If you really know the operator and just don't throw it everywhere without thinking one second

React's entire development philosophy (at least how it appears to me) is to craft an API that prevents developers from shooting themselves in the foot. Sidebar: Google could learn a thing or two from them.

Best practices are best practices for all levels of developers, not just the experts.

Collapse
 
drarig29 profile image
Corentin Girard

I don't like the shortcut where you just evaluate the truthiness of myArray.length to know if an array is empty or not...

Collapse
 
dimaportenko profile image
Dima Portenko

I agree. As Kent C. Dodds said it's abuse of usage && rather than clean code. Also this article provides 0 arguments and just direct you this is good and this is bad.

Some day usage of && in render function will lead you to stackoverflow.com/questions/523683.... Probably you'll not be who wrote it but you have to fix it.

Collapse
 
karladler profile image
Karl Adler

also very bad idea for react native, which crashes if you don't return null

Collapse
 
katherinecodes profile image
Katherine Peterson

I see people do that last one a lot, setting state using the previous state directly instead of using a function. They even do it in the React docs. Do you have an example of when/how this could cause problems?

Collapse
 
thawkin3 profile image
Tyler Hawkins

I sure do! I have some examples hosted here: tylerhawkins.info/react-component-...

You can demo the behavior in the sections titled "BAD: Don't set state that relies on the previous state without using the function of previous state" and "GOOD: When setting state that relies on the previous state, do so as a function of previous state".

Note how the bad example only changes the button's status once when the "Toggle button state 2 times" button is clicked. It goes in this order:

enabled -> disabled
enabled -> disabled
Enter fullscreen mode Exit fullscreen mode

The good example correctly changes the button's status twice like this because it relies on the previous state before each state change is made:

enabled -> disabled
disabled -> enabled
Enter fullscreen mode Exit fullscreen mode

You're right that the React docs show examples of incrementing a counter directly without setting it as a function of the previous state, and it drives me crazy! Haha.

The thing to note is that you will only run into trouble if the state updates are batched. So most of the time, you'll be fine writing the code without using a function. But, you will always be safe if you choose to write the code the correct way if it relies on the previous state, even if the state updates are batched. So it's safer to write it this way.

Collapse
 
katherinecodes profile image
Katherine Peterson

Thanks, I appreciate the examples!

Collapse
 
lequanghuylc profile image
Lê Quang Huy • Edited

Thanks for writing this article. In my opinion, clean code also means it's easier to upgrade it to be the more complex code, it'll be clean when you have your reasons ( and be sure other maintainers know your reasons via docs).

1 & 2 Conditional rendering

in order to avoid weird crash on some Android devices (assuming that we all try React Native someday), this is what I use all the time

<>
 {Boolean(showConditionalText) && <ConditionalTextComponent />}
</>
Enter fullscreen mode Exit fullscreen mode

This will prevent someone to assign a text variable to showConditionalText, it's fine on the web because we can render text as direct child.

4 String props

if you use curly braces, it'll be more convenient to convert it to variable later, and you will convert alot

// from
<Greeting personName={"John"} />
// to
<Greeting personName={userInfo.name} />
Enter fullscreen mode Exit fullscreen mode

It's more like Class Component vs Functional Component in the past, everyone wants to write a functional component in the beginning, but ends up converting it to class component.

8

Just want to share another way to deal with states, it does not relate much to your example. It's when you have dozen of useState variables, and the docs recommend useCallback, so the function always have most updated values. If keeping track of when to use useCallback or normal function, keeping track of the values cost you too much time, you can use a mutable variable and you can access anytime without useCallback

const [entityAData, setEntityAData] = useState(initialValueA);
const [entityBData, setEntityBData] = useState(initialValueB);

const entityADataRef = useRef(initialValueA);
const entityBDataRef = useRef(initialValueB);

useEffect(() => {
  entityADataRef.current = entityAData;
}, [entityAData]);
useEffect(() => {
  entityBDataRef.current = entityBData;
}, [entityBData]);

// entityADataRef.current and entityBDataRef.current will always be updated. free to use it as ready only variable, without callback

// we can write a custom hook to keep it short and avoid repeating code
Enter fullscreen mode Exit fullscreen mode
Collapse
 
mrchedda profile image
mr chedda

Boolean(value) === !!value. The latter is just more concise

Collapse
 
lequanghuylc profile image
Lê Quang Huy

True. But the article was saying about showConditionText, not !!showConditionText. Without the way to force the variable to be boolean, it will cause problem if the variable somehow is assigned to text or object.

And Boolean() is more convenient when doing a complex comparison (more than one)

Thread Thread
 
mrchedda profile image
mr chedda

I’m not sure you understand. I’m commenting on your comment. You suggested to use Boolean(showConditionText) instead of the raw variable: showConditionText. I’m only pointing out that !!showConditionText is a more concise syntax of Boolean(showConditionText). Pre-pending any variable with !! Is implicitly coercing it to a Boolean. Further more Boolean() only takes one argument so I’m not sure how it handles more “complex” comparisons?

Thread Thread
 
lequanghuylc profile image
Lê Quang Huy

Complex means more than one variable. Often goes with && or ||

For example Boolean(a && b && c). In order to write !!(a && b && c), if you start with !!a you need to add “(” before a and add b c after a and I dont like that. I just want to put the cursor inside the “()” and write new variables.

If you understand my comment, you’ll know I talk about the possibility of expanding the code from simple to be more complex logic.

Thread Thread
 
mrchedda profile image
mr chedda • Edited

That's not what your suggestion was but in that case it would simply be

( (!!a && !!b && !!c) || !!d ) && <Component />
Enter fullscreen mode Exit fullscreen mode

Your suggestion above would result in

( (Boolean(a) && Boolean(b) && Boolean(c)) || Boolean(d) ) && <Component />
Enter fullscreen mode Exit fullscreen mode

which is not efficient nor concise

Thread Thread
 
lequanghuylc profile image
Lê Quang Huy

@mr chedda
The first one is too many !!
The second one, is funny, because I literally gave my example above. How could you see my example and come up with that?
If you mean “result in” is how the computer sees it, all of your examples, my examples and Ryan’s examples, are the same to the computer.
And if you mean “result in” is the way that I actually write it. I will write it again and hope you can get it

Boolean((a && b && c) || d) &&

Thread Thread
 
mrchedda profile image
mr chedda

Lê.... Maybe it's because English is NOT your first language but the two statements I wrote are programmatically equivalent but your syntax is written in a more beginner way.

You likely WILL NOT see this in a production code base written by senior developers.

( Boolean(a) && Boolean(b) && Boolean(c)) || Boolean(d) ) && <Component />
Enter fullscreen mode Exit fullscreen mode

You likely WILL see this syntax in a production code base written by senior developers:

( ( !!a && !!b && !!c ) || !!d ) && <Component />
Enter fullscreen mode Exit fullscreen mode

I don't know how I can make it any clearer.... the two statements are saying THE EXACT SAME THING... the difference is your suggestion using Boolean() would likely be written by a beginner programmer.

Thread Thread
 
lequanghuylc profile image
Lê Quang Huy

So your comment is about:

  1. You wrote an example with multiple “Boolean()” and assume I came up with that, when in two of my examples, multiple variables inside just one “Boolean()”

  2. repeated what I said about “the exact same thing”, yes the computer would understand them the same way, we’re talking about Coding style, the convenient of upgrading the code to more complex logic.

  3. Didnt have any reply to my “too many !!”. Made an execuse the seniors are using it, production code.. bla bla

  4. Personal attack about English not my native language, beginner level

Keep it up, you’re doing real great. Dont worry too much, I’ve been through many years of js development, your comments and your capital letters wont make me a beginner.

Collapse
 
pozda profile image
Ivan Pozderac • Edited

What are your thoughts about having utility component for conditional rendering like this?

<ConditionalComponent if={!!achievementsList}>
    <Achievements data={achievementsList} />
</ConditionalComponent>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
adamashored profile image
adam-ashored • Edited

This is a fantastic pattern - just not for trivially simple logic.

It's a really great for passing in parameters to a component that might use some context (or other data source) whereas your parent component doesn't need to use that context. It allows all of the logic of conditional rendering to be then tucked away in one place and tested separately.

Collapse
 
pozda profile image
Ivan Pozderac

great remark, I will have to do my own research on that as your comment gave me some ideas and a different perspective on the whole thing.

Thread Thread
 
adamashored profile image
adam-ashored • Edited

By the way, this is far from a "novel" pattern, but it sounds like it might be a lightbulb moment for you in terms of separating concerns.

Take a look at some very popular conditional components that you've probably already used:

MaterialUI's Hidden component: github.com/mui-org/material-ui/blo...

It uses the theme context internally and the media-query props you provide it to determine if a child should be rendered.

React Router's Route component: github.com/ReactTraining/react-rou...

It uses the router context and the path parameter you pass in to determine if the child (or render of Component props) should be rendered.

Collapse
 
thawkin3 profile image
Tyler Hawkins

Interesting! I think the only time I've used an approach like that is when using something like react-responsive and their MediaQuery component, which is basically the same idea. But for code I control, I can't say I've ever reached for this technique.

What are your thoughts? Do you like this pattern better than explicitly using ternaries or && operators?

Collapse
 
pozda profile image
Ivan Pozderac

I think it is neat way to handle conditional rendering that is OK to use for simpler cases only.

I found parts of the app that had that utility component stacked one on top of the other with various different conditions and it felt bizarre and too hacky.

I used it only recently in the code base that already had it. In all other projects I am more comfortable with using && and ternaries as I find them easier to read and understand what is going on, also feels more natural to me.

Collapse
 
lpyexplore profile image
Lpyexplore

Hello, I am a front-end enthusiast and I am from China. I just read your article and think it is very good. So I want to translate your article into Chinese and publish it on a Chinese blog site. I have nearly 20,000 fan readers. I want to share your article with more people. I want to ask for your information. Opinion, can I do this? Hope to get your reply!

Collapse
 
thawkin3 profile image
Tyler Hawkins

Sure thing! As long as you include the link to this original post, that's fine with me. Can you comment back here with your translated blog post URL once you publish it?

Collapse
 
lpyexplore profile image
Lpyexplore

Of course! Thank you for agreeing to translate this article. After I translate and publish the article, I will attach a URL below this comment. And I will attach a link to the original text in the article

Collapse
 
georgesofianosgr profile image
George Sofianos

Great post! I agree to all your suggestions excluding the second one. I find the ternary operator less readable than conditional rendering. Character ":" is difficult to notice between multiple lines. Why do you think that the ternary operator is the better option ?

Collapse
 
thawkin3 profile image
Tyler Hawkins

Thanks! Good question.

To me, the ternary helps show that these two pieces of UI are related. When true, you render X, and when false, you render Y.

    <div>
      <button onClick={handleClick}>Toggle the text</button>
      {showConditionOneText ? (
        <p>The condition must be true!</p>
      ) : (
        <p>The condition must be false!</p>
      )}
    </div>
Enter fullscreen mode Exit fullscreen mode

By splitting these out into two separate conditionals using the && operator, it obfuscates the fact that the two pieces of UI are related and that they are opposites of each other.

    <div>
      <button onClick={handleClick}>Toggle the text</button>
      {showConditionOneText && <p>The condition must be true!</p>}
      {!showConditionOneText && <p>The condition must be false!</p>}
    </div>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
joaorafaelsantos profile image
João Santos

I agree. I've been refactoring several React components lately, and ternary operators (in my opinion) make the code noisy.

While two separate conditionals may indicate these pieces are not related, I think it's easier to read and understand.

Collapse
 
mrchedda profile image
mr chedda

Writing two separate conditionals for conditionally rendering two components, is inefficient not to mention rudimentary programming. Ternary syntax is much more concise and expected from a dev working in a production application IMHO. Imagine a switch statement writing the same n case statements for 1 result instead of using fall through. It may be more “readable” in terms of leisure reading but not pragmatic programming in any way.

Collapse
 
garystorey profile image
Gary Storey

For those looking for a short simple solution to conditional rendering:

const Show = ({when=false, children}) = when ? <>{children}</> : null
Enter fullscreen mode Exit fullscreen mode

and usage in your component:

const [show, setShow] = useState(false);
<Show when={show}>
Hello!
</Show>
Enter fullscreen mode Exit fullscreen mode

It uses a Fragment as a wrapper in case the user does not have a single parent element in children

Collapse
 
urielbitton profile image
Uriel Bitton

Great tips man! however i'm not sure the example with setValue(!value) is necessarily bad. For booleans we don't care what the previous value is bc we know it is either true or false so simply setValue(!value) always does the trick. However with any other data types it is necessary to do setValue(prev => prev+1) etc.

Collapse
 
thawkin3 profile image
Tyler Hawkins

Not quite! When reversing a boolean value, we absolutely do care what the previous value was. That's the exact same logic behind incrementing a counter which you provided as a counterexample.

Doing toggleValue(!value) rather than toggleValue(value => !value) is the equivalent of doing incrementCounter(value + 1) rather than incrementCounter(value => value + 1).

In both cases, the first set of implementations is risky and can lead to bugs, while the second set of implementations will be bug free even if state updates are batched.

You can verify this by trying out the two code snippets from my article. I have them hosted here on this page under the sections titled "BAD: Don't set state that relies on the previous state without using the function of previous state" and "GOOD: When setting state that relies on the previous state, do so as a function of previous state".

tylerhawkins.info/react-component-...

Note how the bad example only changes the button's status once when the "Toggle button state 2 times" button is clicked. It goes in this order:

enabled -> disabled
enabled -> disabled
Enter fullscreen mode Exit fullscreen mode

The good example correctly changes the button's status twice like this because it relies on the previous state before each state change is made:

enabled -> disabled
disabled -> enabled
Enter fullscreen mode Exit fullscreen mode
Collapse
 
urielbitton profile image
Uriel Bitton

I've used the way i showed you in many apps and it always works, never had it bug once. I'm not sure what you are doing in your example but this does not happen.

Thread Thread
 
thawkin3 profile image
Tyler Hawkins

Not quite, again. The examples are the code snippets provided in this article, and the demo is found on the site I posted. So you can easily verify this.

The thing to note is that you will only run into trouble if the state updates are batched. So most of the time, you'll be fine writing the code without using a function, like you said. But, you will always be safe if you choose to write the code the correct way if it relies on the previous state, even if the state updates are batched. So it's safer to write it this way.

Collapse
 
willsmart profile image
willsmart

Great tips!
Have you used the wrong code for the first two code snippets though? There's ConditionalRenderingBad and StringPropValuesGood both of which seem a bit out of place.

Collapse
 
thawkin3 profile image
Tyler Hawkins

Thanks for the heads up! It looks like something funky is going on with either Dev's liquid tags markdown or GitHub's gists, because whenever I see them out of order in the article, refreshing the page puts them in the correct place. So I can confirm the markdown and gist urls themselves in the markdown are correct, the wrong urls are just sometimes being rendered... How strange.

Collapse
 
davejsaunders profile image
Dave Saunders

This is great, thank you.

I didn't know about #1, and always hated the ternary with the null at the end.
Now I can go back and fix them all🙂

Collapse
 
travisblair profile image
Travis Blair

It's a really nice shortcut that I'm a big fan of, but be aware of the gotchas that come along with it!

Collapse
 
thawkin3 profile image
Tyler Hawkins

You're welcome. Glad this helped!

Collapse
 
lpyexplore profile image
Lpyexplore

Hello, I am a front-end enthusiast and I am from China. I just read your article and think it is very good. So I want to translate your article into Chinese and publish it on a Chinese blog site. I have nearly 20,000 fan readers. I want to share your article with more people. I want to ask for your information. Opinion, can I do this? Hope to get your reply

Collapse
 
vedovelli profile image
Fábio Vedovelli

Thanks for the article. I see several people advocating against short-circuiting with &&, as you stated in your first example. By using short-circuit you run into the risk of ending up with a rendered the number 0 (zero) instead of your component.

Collapse
 
thawkin3 profile image
Tyler Hawkins

Thanks Fábio! That's true, to an extent. There was some good discussion in another thread and a reference to a Kent Dodds blog post where he advocates against using &&.

I do think it's important to note that the issue isn't necessarily with short-circuiting and the && operator, but rather from misusing it or misunderstanding how short-circuiting works.

For example, you can avoid rendering "0" in your UI from writing myArray.length && <MyComponent /> by instead using any of the following:

  • myArray.length > 0
  • !!(myArray.length)
  • Boolean(myArray.length)

But it's true that using the ternary makes it so you don't have to think about this and can just write: myArray.length ? <MyComponent /> : null.

So it really comes down to personal preference and the tradeoff of whether you like having the extra : null in your code or if you can remember to be careful in how you evaluate the length of an array in your conditional.

Collapse
 
developeratul profile image
Minhazur Rahman Ratul

Useful post :)

Collapse
 
thawkin3 profile image
Tyler Hawkins

Thank you!

Collapse
 
ayabouchiha profile image
Aya Bouchiha

Very helpful

Collapse
 
thawkin3 profile image
Tyler Hawkins

Thanks!

Collapse
 
ridhikgovind profile image
Ridhik Govind

Really great tips - Especially the 5th point ! Thanks for this

Collapse
 
thawkin3 profile image
Tyler Hawkins

Thank you! Glad you found it helpful.

Collapse
 
theroka profile image
theroka

Concise summary, thanks!

Collapse
 
thawkin3 profile image
Tyler Hawkins

You're welcome!

Collapse
 
angeloanolin profile image
Angelo Anolin

Really liked the suggestions with regards to the conditional rendering. Slowly refactoring some codes I have written to follow that. Great article!

Collapse
 
thawkin3 profile image
Tyler Hawkins

Thank you!

Collapse
 
gene profile image
Gene
  • Lint your code. It helps make code cleaner as well.

Thanks for this!

Collapse
 
thawkin3 profile image
Tyler Hawkins

You're welcome! Absolutely, linters and auto-formatters are a must. Every project I work on includes ESLint and Prettier, and if it's not already there in a repo I inherit, that's one of the first things I add!

Collapse
 
dhanush72 profile image
Dhanush

Nice hacks!

Collapse
 
thawkin3 profile image
Tyler Hawkins

Thanks!

Collapse
 
tomaszs2 profile image
Tom Smykowski

Id love to see these rules for Assistan extension for Vscode marketplace.visualstudio.com/items...

Collapse
 
sebavuye profile image
Sebastian Vuye

Nice tips!

Adding a linter to your project can also help , as I recognize many lint warnings/errors in your tips.

Collapse
 
thawkin3 profile image
Tyler Hawkins • Edited

Thanks! I definitely agree that using a linter in any codebase is a must. You'll be happy to know that I used ESLint and Prettier in my project while preparing this article.

Linters will only go so far though, and they'll only catch the things that you configure them to.

For example, using the ESLint settings that react-scripts uses from create-react-app alongside my Prettier configuration, the only error caught in all of my code snippets above are the use of double quotes inside the curly braces in the fourth bad example.

Linters also won't be able to catch other code smells that only humans can see, such as bad variable names or confusing logic in the code. Ultimately it takes a human to ensure that the code is clean.

Collapse
 
achinara profile image
Chinara

Could you tell more about the 7th item? What does "Undefined details excluded" mean, is it from the docs?
I usually assign a default value to the props, like this ({someBooleanProp = false})

Collapse
 
p10 profile image
p10

clean code !== pretty code
This article is about pretty code

Collapse
 
mavortius profile image
Marcelo Martins

Good article, but those are tips for beginers anyway.