Skip to content

Latest commit

 

History

History
409 lines (338 loc) · 8.73 KB

File metadata and controls

409 lines (338 loc) · 8.73 KB
layout default
title React Style Guide

EY React Style Guide() {

A mostly reasonable approach to React

Note: this guide is based on Airbnb guide Airbnb React Style Guide, it reinforces the most important rules and revises some of them.

Table of Contents

  1. Methods
  2. Hooks
    • useMemo
    • useCallback
    • useEffect
  3. Naming Conventions
    • Component naming
    • File naming
    • Const and variable naming
  4. Components
    • Component structure
    • Props
    • Imports

Methods

  • Method placement: if the method doesn't use this, it can be a global function at the compilation unit.

  • Method declaration in pure functions: method declarations should be outside pure functions.

Why? Methods are created in every execution of the render.

// bad
const UserDetails = ({states, user}) => {
    const findState = (states, user) => find(states, state => state._id == user.state);
    return (
        <div>
            {findState(states, user).name}
        </div>
    );
};

// good
const findState = (states, user) => find(states, state => state._id == user.state);

// bad
const UserDetails = ({states, user}) => (
    <div>
        {findState(states, user).name}
    </div>
);

// good
const findState = (states, user) => states.find(state => state._id == user.state);

  • Methods and props naming: handlers of a component should be called handler+event.
// good
const handleSubmit = () => {
    // ...
}

const handleChange = (e) => {
    // ...
}

  • Child methods naming: handlers got from props should be named on+event.
// bad
const UserDetails = ({submit, setChange}) => {
    //...
}

// good
const UserDetails = ({onChange, onSubmit}) => {
    //...
}

⬆ back to top

Hooks

  • useMemo: use useMemo always when the result is used in the render.
// bad
const UserDetails = ({states, user}) => {
    const state = states.find(state => state._id == user.state);
    return (
        <div>
            {state.name}
        </div>
    );
};
// good
const UserDetails = ({states, user}) => {
    const state = useMemo(() => states.find(state => state._id == user.state), [states, user]);
    return (
        <div>
            {state.name}
        </div>
    );
};

⬆ back to top

  • useCallback: use useCallback always when the function is passed to a child component
// bad
const UserDetails = ({onChange}) => {
    const handleChange = (e) => {
        onChange(e.target.value);
    };
    return (
        <input onChange={handleChange} />
    );
};
// good
const UserDetails = ({onChange}) => {
    const handleChange = useCallback((e) => onChange(e.target.value), [onChange]);
    return (
        <input onChange={handleChange} />
    );
};

⬆ back to top

  • useEffect: if the effect is used to fetch data, it should be in a custom hook if not put just before the return.
// bad
const UserDetails = ({userId}) => {
    const [user, setUser] = useState(null);
    useEffect(() => {
        fetchUser(userId).then(user => setUser(user));
    }, [userId]);
    return (
        <div>{user.name}</div>
    );
};
// good
const UserDetails = ({userId}) => {
    const user = useUser(userId);
    return (
        <div>{user.name}</div>
    );
};
// good
const Foo = () => {
    const location = useLocation();
    useEffect(() => {
        //...
    }, [location]);
    return (
        <div>
            {location.pathname}
        </div>
    );
};

⬆ back to top

Naming Conventions

  • Component naming: component files should be named with PascalCase and the component inside the file should be named the same.
// bad
// userdetails.jsx
const userdetails = () => {
    // ...
};
// good
// UserDetails.jsx
const UserDetails = () => {
    // ...
};
  • File naming: files should be named with camelCase. Only the component files should be named with PascalCase.
// bad
// user_details.tsx
// user-details.ts
// userDetails.tsx
// UserDetails.ts

// good
// UserDetails.tsx
// userDetails.ts
// bad
// user_list.jsx
const UserList = () => {
   const users = [];
   //...    
}

// good
// UserList.jsx
const UserList = () => {
    const users = [];
    //...    
}
  • Const and variable naming: use camelCase for variables and UPPER_SNAKE_CASE for constants.
// bad
const maxSize = 10;
const pi_value = 3.14;

// good
const MAX_SIZE = 10;
const PI_VALUE = 3.14;

⬆ back to top

Naming Conventions

  • Component structure: component structure should be as follows:
// bad
const UserDetails = ({user}) => {
    const [state, setState] = useState(null);
    useEffect(() => {
        //...
    }, []);
   const handleChange = (e) => {
      //...
   };
    const renderUser = () => (
        <div>{user.name}</div>
    );
    return (
        <div>
            {renderUser()}
            <input onChange={handleChange} />
        </div>
    );
};

// good
const UserDetails = ({user}) => {
    const [state, setState] = useState(null);
    const handleChange = (e) => setState(e.target.value);

    useEffect(() => {
        //...
    }, []);

    return (
        <div>
            <div>{user.name}</div>
            <input onChange={handleChange} />
        </div>
    );
};
  • Props: props should be destructured in the function signature.
// bad
const UserDetails = (props) => {
    const {user, onChange} = props;
    return (
        <div>
            <div>{user.name}</div>
            <input onChange={onChange} />
        </div>
    );
};
// good
const UserDetails = ({user, onChange}) => (
    <div>
        <div>{user.name}</div>
        <input onChange={onChange} />
    </div>
);

⬆ back to top

  • Imports: Import organization and best practices for React components.

  • Specific imports: avoid importing everything to reduce bundle size. Import only what you need.
// bad
import * as React from 'react';
import * as utils from '../utils';
// good
import { FC, useState, useEffect, useMemo } from 'react';
import { formatDate, validateEmail } from '../utils';

  • Import order: organize imports in the following order with blank lines between groups:
  1. External libraries (React, third-party packages)
  2. Internal modules (components, hooks, utils)
  3. Relative imports (same directory, parent directories)
  4. Type imports (if using TypeScript)
// good
import { useState, useEffect, useCallback } from 'react';
import { Router } from 'react-router-dom';
import { styled } from '@mui/material/styles';

import { Button } from '../components/Button';
import { useAuth } from '../hooks/useAuth';
import { api } from '../services/api';

import './Component.css';
import { validateForm } from './utils';

import type { User, UserProps } from '../types';

  • Default vs named imports: prefer named imports for better tree-shaking and explicit dependencies.
// bad
import React from 'react';
import utils from '../utils';

// good
import { FC, ReactNode } from 'react';
import { formatDate, validateEmail } from '../utils';

  • Dynamic imports: use dynamic imports for code splitting when appropriate.
// good - for large components or libraries
const LazyChart = lazy(() => import('../components/Chart'));
const LazyModal = lazy(() => import('../components/Modal'));

// good - for conditional imports
const loadAnalytics = async () => {
  if (process.env.NODE_ENV === 'production') {
    const { analytics } = await import('../services/analytics');
    return analytics;
  }
};

  • Import aliases: use clear and consistent aliases when needed.
// bad
import { Button as B } from '../components/Button';
import { Utils as U } from '../utils';

// good
import { Button as PrimaryButton } from '../components/Button';
import { DateUtils } from '../utils';

  • Type imports: separate type imports from value imports (TypeScript).
// bad
import { FC, useState, User, UserProps } from 'react';

// good
import { FC, useState } from 'react';
import type { User, UserProps } from '../types';

⬆ back to top