Code style

Codestyle for React applications. Do & Don't.

Codestyle for React applications. Do & Don't.

Writing clean and maintainable code in React can be a challenging task, especially for large-scale applications. To tackle this issue, we need to adopt bulletproof code style in our React applications. In this article I will share with you techniques that we use on many of our projects.

Why is code style important in React applications?

A good code style in React applications has several advantages:

  • Makes the codebase more accessible: A clean and consistent code style makes it easier for developers to understand and navigate the codebase, even if they are not familiar with it.
  • Reduces errors: A consistent code style helps to reduce errors and bugs in the code. It makes it easier to spot mistakes and to write more reliable code.
  • Improves teamwork: A consistent code style makes it easier for team members to work together. They can easily understand each other's code and collaborate more effectively.

What are the essential principles of bulletproof code style in React applications?

  • Consistency: Consistency is the key to a good code style. It means that all code should follow the same conventions and patterns, regardless of who wrote it. This consistency should extend to naming conventions, code formatting, and other stylistic choices.

  • Simplicity: Simplicity is also crucial for bulletproof code style in React applications. It means that the code should be as simple as possible, without sacrificing functionality. This simplicity makes it easier to understand and maintain the codebase.

  • Readability: Readability is another critical principle of bulletproof code style in React applications. It means that the code should be easy to read and understand, even for someone who is not familiar with it. This readability is achieved through the use of clear naming conventions, descriptive comments, and well-organized code.

  • Modularity: Modularity is essential for bulletproof code style in React applications. It means that the code should be organized into small, reusable modules. These modules should be independent of each other, making it easier to modify and maintain the codebase.

Examples

General

No export default

Why? By avoiding the export default, we’ll be obligated to import the component under the same name in all the files, meaning we’ll:

  1. be more coherent
  2. avoid typos
  3. find the component usages easily

Consts

For each feature directory, define the consts in a separate file called consts.ts

No redundant context

Do not repeat unnecessary words in the file name/properties.

// Don't
../components/CheckedFilter/CheckedFilterMenu/CheckedFilterMenuItem.tsx
// Do
../components/CheckedFilter/Menu/Item.tsx

// Don't
const bill = {
	billDetails: "Yo yo whatsup dog",
};
// Do
const bill = {
	details: "Yo yo whatsup dog",
};

Do not use moment.js use date-fns

Since moment.js is deprecated we use date-fns as an alternative.

// Don't
const date = moment().format('yyyy-mm-d');
// Do
import { format, getYear, isBefore } from 'date-fns';
import { DATE_FORMATS } from 'src/utils/date-fns';

format(new Date(), DATE_FORMATS.monthShortWithDateAndYear)

Number of paramethers

Prefer to define parameters of a function as object when there are more than 2 parameters (and not parameters list).

Maintaining a parameter list is hard when you have more than 2 parameters, because you have to be strict about their order. And dealing with default values when you want to add new parameters is no fun.

On the other hand, objects give you a lot more space and freedom, and are a lot more self-explanatory (because they force you to mention the properties of the object when you pass the values to the function).

// Don't
const a = (paramA, paramB, paramC) => {
	bla bla bla...
};
// Do
const a = ({ paramA, paramB, paramC }) => {
	bla bla bla...
};

// Don't
const createFlower = (shouldCreateSomeBlaBla) => {
	bla bla bla...
};

// I have no idea what `true` stands for
createFlower(true);
// Do
const createFlower = (params: { shouldCreateSomeBlaBla: boolean }) => {
	bla bla bla...
};

createFlower({ shouldCreateSomeBlaBla: true });

Utils

Do not mix tsx and ts files:

  • TSX - will be a component file that returns a component - it won’t be a util function that returns a component.
  • TS - will be used as a util\const\style\type file and will only include javascript (and not React).

Each of the util files should be placed under the appropriate page\ component\ module. We want to make sure that the util file is placed in the most relevant location.

Utils should be placed at the lowest common level possible if they are used in more than one place.

Utils that have multiple uses should be at the lowest common level possible.

Redux

When writing new REST slices, it is preferable to use separate slices for REST operations rather than using a common wrappers.

A good example:

// Don't
import { createRestfulSlice } from 'src/helpers/redux/createRestfulSlice';
import { API } from './api';

const someStore = createRestfulSlice({
   api: API,
});
// Do
import { createFetchSlice } from 'src/helpers/redux/restFetchSlice';
import { hashListKey } from 'src/helpers/redux/createRestfulSlice';
import { createListSlice } from 'src/helpers/redux/restListSlice';
import { createDeleteSlice } from 'src/helpers/redux/restDeleteSlice';
import { getProApi } from 'src/modules/get-pro/api';
import { name } from './consts';

// fetch
export const proPaidPaymentsFetchSlice = createFetchSlice({
  storeName: name,
  actionName: 'proFetch',
  api: getProApi.paid.fetch,
});

// list
export const proInvoicesListSlice = createListSlice({
  storeName: name,
  api: getProApi.invoices.list,
  actionName: 'proList',
  listHashFunc: hashListKey,
});

// delete
export const proPaidPaymentsMarkAsUnpaidSlice = createDeleteSlice({
  storeName: name,
  api: getProApi.paid.markAsUnpaid,
  actionName: 'proMarkAsUnpaid',
});

Components

Prefer to use functional components over Class components.

Prop types should sit inside the component (unless they are shared).

// Don't
export type SomeComponentPropsType = {
	// ...
}

import { SomeComponentPropsType } from '..'

const myComponent = ({...}: SomeComponentPropsType) => ...
// Do
type Props = {
	// ...
}

const myComponent = ({...}: Props) => ...

Avoid using render functions of components inside a component.

Hooks

We do not export components from hooks.

Refrain from using HOC - use hooks instead.

// Don't
const ListPage = compose(withNavigator())(ListPageComp);
// Do
const ListPage = ({ ...rest }: ListPageProps) => {
	// This is the prop that was passed from the HOC
	const location = useLocation();

  return (
    <ListContainer
      path={location.pathname}
    >
      <ListContent>
       ...
    </ListContainer>
  );
};

Naming

Constants

Constants files will be called consts.ts.

Import specific consts and not the whole object.

// Don't
import { CONSTS } from 'src/utils/consts';
// Do
import { CURRENCY } from 'src/utils/consts';

Actions

Prefer naming actions with handle prefix.

// Don't
const click = () => ...

onClick={click}
// Do
const handleClick = () => ...

onClick={handleClick}

Types

When writing types (excluding props types) use the suffix Type.

// Don't
export type Payment = {
	// ...
}
// Do
export type PaymentType = {
	// ...
}

Enums

Only export enum and not const enum:

// Don't
export const enum Bool {
  True,
  False,
  FileNotFound
}
let value = Bool.FileNotFound;
// value will transpile to 2

let value = 2 /* FileNotFound */
// Do
export enum Bool {
  True,
  False,
  FileNotFound
}
let value = Bool.FileNotFound;

No need to create a type for an enum unless you need specific values from the enum.

Enums should be written in Pascal's case:

// Don't
enum SOME_ENUM { ... }
// Do
enum SomeEnum { ... }

Design system

It's important to start using a design system in your project and setup it using storybook. More on this in the next articles.

Conclusion

In conclusion, adopting a bulletproof code style is crucial for building maintainable and scalable React applications. Consistency, simplicity, readability, and modularity are essential principles that can help you achieve this goal.

A blog for self-taught engineers

Сommunity is filled with like-minded individuals who are passionate about learning and growing as engineers.