Skip to content

Instantly share code, notes, and snippets.

@jamstooks
Last active March 9, 2022 18:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jamstooks/10ebb174d10db4590be148a6f2acbc26 to your computer and use it in GitHub Desktop.
Save jamstooks/10ebb174d10db4590be148a6f2acbc26 to your computer and use it in GitHub Desktop.
React Rules of Thumb

Rules of Thumb

For React Applications and Others

Brevity, but not at the expense of clarity

Consistency, but not at the expense of improvement

Less Markup

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>

Components should "Do One Thing"

Keep components simple and the cognitive load simple, like:

  • "fetch data and transform it"
  • "show a menu"

Short components

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.

DRY3 - Don't repeat yourself.... (thrice)

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?"

index.* is for indexing ONLY

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.

Use a package mentality (modularity)

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".

Keep things close to where they are used

You'll thank yourself later when you look at a component later and don't have to dig around for all the connections.

Go Generic

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} />

Styles belong with components

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.

Lift up State

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 clutter the global state.

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.

Keep logic out of markup

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>
  );
}

Beware of constants.js or globals.js

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.

Short functions; use less lines of code

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.

Limit return statements

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;
}

Pass State Down, not Over

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!

Declarative over implemented

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

Don't reinvent the wheel - use a package

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).

Variable naming

Use verbs for functions and booleans and on/handle for events.

Reduce line count

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

Reduce File Counts

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.

Don't over-use optimizations like useCallback and useMemo

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.

When to useMemo and useCallback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment