For React Applications and Others
Brevity, but not at the expense of clarity
Consistency, but not at the expense of improvement
Keep the markup in components to a minimum. Break components up by logical/visual.
<Header>{title}</Header>
is much cleaner than
<div className="">
<span>{title}</span>
</div>
Keep components simple and the cognitive load simple, like:
- "fetch data and transform it"
- "show a menu"
This reduces the cognitive complexity and improves at-a-glance clarity.
In fact, 25 is ideal, anything over 100 is way too much. CodeClimate's default threshold is 25.
I used to be in the DRY-EVER! camp, but now I'm in the "if you're repeating yourself, just think about why and if it's necessary... could this be simpler?"
this includes
index.test.*
Keep index files to import and export functions only. A quality of life reason for this is that the filename isn't helpful in many IDE's as the tabs are hard to distinguish. At the application level, there are good arguments for breaking componenents up into smaller chunks that can be exported and tested separately.
Build components with an eye for reuse. What does the consuer of the component nead to know? If you were packaging this for npm, how would you change your props? There's a danger to getting too flexible here, but generally speaking... if you're thinking about distribution-level components you'll be following best practices around documentation, testing, separation of concerns, etc...
This include bundling styles, configs, utils, etc that only apply to a component. Don't separate them, unless they are global to the application. See Beware of constants or globals".
You'll thank yourself later when you look at a component later and don't have to dig around for all the connections.
By focusing on reuse, we can reduce duplication. For example, use a Header
component that's shared between multiple views should take relevant props, not an object:
<Header title="title" subtitle="sub" />
is better than
<Header article={article} />
Compartmentalize our code. If a component has specific styles, think twice before you pass those down as a className
from the parent. Is that really necessary or can the component take care of itself. Think composition.
Note, theming is likely a better approach, if you need customizations.
Wherever possible, keep state out of low-level components. This is closely related to Go Generic.
See "Don't clutter the global state" for the corallary.
Don't lift state any higher than you need it. Otherwise, you're stuck with deeply nested prop-passing or other mechanisms that add unecessary complexity.
Do the work first:
Good:
const MyComponent({first, second}) => {
const avg = (first + second) / 2;
return <p>{ avg }</p>
}
Bad:
const MyComponent({first, second}) => {
return (
<p>
{ (first + second) / 2 }
</p>
);
}
constants
, globals
, and utils
are really ambiguous names and there's nothing worse than a 1000 line file that looks like an abandoned storage unit.
Of course, we always need some sort of placeholder for these things, but it's more declarative to contain them in folders that describe their use, like graphql/utils.js
or services/api/config.js
or MyComponent/utils.js
("Keep things close to where they are used"). Another approach is use them as folders themselves with clarifying filenames, like utils/formatting.js
.
Consider config
over constants/globals
. Break up configs to be specific, like config/commonText.js
.
Brevity, but not at the expense of readability
If a function is longer than ~25 lines, consider breaking it up with helper functions, hooks, or more concise JS syntax, like ternaries, or nullish coalescence, or chaining.
Sometimes we loose readability with radical truncation, so be mindful of how complex your syntax is... we're not looking for "tricks" here.
Functions that have just one line, don't need a return
:
const myFunc = (a) => a * 2;
Utilize a ternary:
const myFunc = (a) => a ? a * 2 : 0;
Don't:
const myFunc = (a) => {
if(!a) return 0;
return a * 2;
}
You'll generally want to pass an individual prop down from a partent component. So, if you have a routed BookList
and BookDetail
components, your BookList
should pass the bookId
property to BookDetail
.
Then, if BookDetail
has a link to another book, the navigation action should update state in BookList
, not use your Router to pass state in the link.
BookDetail
should only worry about "doing one thing:" displaying a book, not around the logic of navigation.
If you need to pass state over, use Context
... but use it sparingly!
If you find you're using a lot of switch statements or nested ifs, then you probably want to look at createing a "framework" for your component or function. Maybe there's an object structure that you can use to declare your functionality?
Ideally, when you want to add a new option or step, it shouldn't involve much noew code, just and updated configuration.
A very common example is something as simple as creating a Row
component for use in a Table
component instead of looping through and rendering <tr><td>...</td></tr>
.
// TODO more examples
The React ecosystem is solid and most problems have been solved. Before you build your own hook, just look around and make sure it hasn't already been done (just make sure it's being maintained).
Use verbs for functions and booleans and on
/handle
for events.
Every single line has a cost.
Keep things brief:
- destructure function props
- use maps instead of loops
- use indexes instead of
switch
statements - use single-line options where possible
Any directory shouldn't have more than 5 (max 10) files or other directories under it. Giant src/components
folders with all your components, for example, are very difficult to navigate.
Break large folders up into multiple subfolders. Think about how you can better structure your code so you keep logic close to where it's used, but also break things up into just a handful of key conceptual elements. For components, maybe that is forms
, layout
, and pages
or something. For third-party configs, consider collecting them all in something like src/services/
(with api
, logging
, and caching
, for example) instead of at the top-level or src
, or worse, one big config.[js|ts]
file.
Essentially, you want another developer to understand your structure at a glance and never open a directory to find more files than they can grok in just a few seconds.
There's definitely a place for these, but they are also easy to overuse and don't always bring benefits, but don't just trust me, trust Kent C. Dodds (with my emphasis):
I would like to re-iterate that I strongly advise against using React.memo (or it's friends PureComponent and shouldComponentUpdate) without measuring because those optimizations come with a cost and you need to make sure you know what that cost will be as well as the associated benefit so you can determine whether it will actually be helpful (and not harmful) in your case, and as we observe above it can be tricky to get right all the time so you may not be reaping any benefits at all anyway.