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 eight ways we can write cleaner React code.
In going through these suggestions, it’s important to remember that that’s exactly what these are: suggestions. If you disagree with any of them, that’s completely fine. However, these are practices that I’ve found helpful in writing my own React code. Let’s dive in!
1. Conditional rendering only for one condition
If you need to conditionally render something when a condition is true
and not render anything when a condition is false
, don’t use a ternary operator. Use the &&
operator instead.
Bad example:
Good example:
2. Conditional rendering on either condition
If you need to conditionally render one thing when a condition is true
and render a different thing when the condition is false
, use a ternary operator.
Bad example:
Good example:
3. Boolean props
A truthy prop can be provided to a component with just the prop name without a value like this: myTruthyProp
. Writing it like myTruthyProp={true}
is unnecessary.
Bad example:
Good example:
4. String props
A string prop value can be provided in double quotes without the use of curly braces or backticks.
Bad example:
Good example:
5. Event handler functions
If an event handler only takes a single argument for the Event
object, you can just provide the function as the event handler like this: onChange={handleChange}
. You don't need to wrap the function in an anonymous function like this: onChange={e => handleChange(e)}
.
Bad example:
Good example:
6. Passing components as props
When passing a component as a prop to another component, you don’t need to wrap this passed component in a function if the component does not accept any props.
Bad example:
Good example:
7. Undefined props
Undefined props are excluded, so don’t worry about providing an undefined
fallback if it's ok for the prop to be undefined.
Bad example:
Good example:
8. Setting state that relies on the previous state
Always set state as a function of the previous state if the new state relies on the previous state. React state updates can be batched, and not writing your updates this way can lead to unexpected results.
Bad example:
Good example:
Honorable Mention
The following practices are not React-specific but rather are good practices for writing clean code in JavaScript (and any programming language, for that matter).
- Extract complex logic into clearly-named functions
- Extract magic numbers into constants
- Use clearly named variables
Happy coding!
Discussion (44)
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?
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:
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:
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.
Thanks, I appreciate the examples!
"Use the && operator instead." not sure about this. see kentcdodds.com/blog/use-ternaries-...
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 :
This code is incorrect and should check
score !== undefined
instead.Agreed! Kent points this out in his article too that his downfall was using
contacts.length
in his code rather thancontacts.length > 0
or!!contacts.length
orBoolean(contacts.length)
, not necessarily the usage of the&&
operator.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
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.
The
length
thing, you should never use (with 0 being falsy).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.
I don't like the shortcut where you just evaluate the truthiness of
myArray.length
to know if an array is empty or not...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 ?
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.
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.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.
What are your thoughts about having utility component for conditional rendering like this?
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?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.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.
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 thantoggleValue(value => !value)
is the equivalent of doingincrementCounter(value + 1)
rather thanincrementCounter(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:
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:
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.
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.
Great tips!
Have you used the wrong code for the first two code snippets though? There's
ConditionalRenderingBad
andStringPropValuesGood
both of which seem a bit out of place.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.
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🙂
It's a really nice shortcut that I'm a big fan of, but be aware of the gotchas that come along with it!
You're welcome. Glad this helped!
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
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
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
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.
Really great tips - Especially the 5th point ! Thanks for this
Thank you! Glad you found it helpful.
Very helpful
Thanks!
Concise summary, thanks!
You're welcome!
Really liked the suggestions with regards to the conditional rendering. Slowly refactoring some codes I have written to follow that. Great article!
Thank you!
Nice hacks!
Thanks!
Useful post :)
Thank you!
Thanks for this!
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!
Nice tips!
Adding a linter to your project can also help , as I recognize many lint warnings/errors in your tips.
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 fromcreate-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.
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})
Great post! I agree to all your suggestions excluding the second one.