SaaS template Coding Conventions

An explanation of the coding conventions used in the boilerplate

React.FC vs React.FCC

Since React 18, React.FC no longer implies that components can accept a children prop. The alternative to that is using the interface React.FC<React.PropsWithChildren<{}>>, which is a bit long.

React.FCC is a shorthand that includes children as a possible property that works similarly to how it used to work. When a component needs children, React.FCC is preferred since it's faster to type.

Exporting functions vs constants

We use both conventions in the boilerplate, i.e., exporting functions as components and constants.

My general rule is the following:

  • if it's the exported component, use const typed with React.FC or React.FCC
  • if it's not exported, use function since my personal preference is to define functions below the exported component

NB: sometimes, that may not be the case, and that's not necessarily intentional. The two constructs are essentially the same in most cases.

Why is useCallback used in most components?

Most functions defined within a render function use useCallback. In my experience, this is the better default to avoid re-renders that I can't immediately explain.

Whether you want to adhere to this convention is totally up to you; just be aware of the potential issues.

Interfaces, Types, and Inline

I've gone through phases, so you may find some types defined as type and others as interface. I've since realized that I prefer interface, but you will find some instance that uses type.

Inline types are typically only defined when the type isn't reused anywhere. For example, when defining a function that accepts parameters as objects.

Function parameters

I adhere to the principle that if a function accepts multiple parameters of the same type, then use an object. Unfortunately, Typescript can't save us if the types match.

Consider the example below:

function makeDiv(width: number, height: number) {}
const height = 10;
const width = 40;
// correct according to TS, not in practice!
const div = makeDiv(height, width);

Now we rewrite it using an object:

function makeDiv(params: { width: number, height: number}) {}
const height = 10;
const width = 40;
// Okay, now it looks better!
const div = makeDiv({ height, width });

Custom React Hooks

The boilerplate uses Custom React Hooks when:

  • fetching/writing using an API function
  • fetching/writing using Firestore
  • fetching/writing using Storage

It makes the code more understandable and reusable.