Lesson 58 of 60 – React Best Practices
97%

React Best Practices

React best practices are recommended approaches for writing React applications that are readable, reusable, maintainable, predictable, and easier to debug. Good practices become especially important as an application grows and contains many components, pages, API calls, and developers.

Note: Best practices should make an application easier to understand and maintain. Avoid adding unnecessary complexity simply because a technique is popular.

1. Use Reusable Components

Create components that can be reused in different parts of an application.

function Button({ title }) {

    return (
        <button className="btn btn-primary">
            {title}
        </button>
    );
}

The same component can be used for different buttons.

2. Keep Components Focused

A component should generally have a clear responsibility.

Dashboard
 ├── Header
 ├── Sidebar
 ├── Statistics
 ├── UserList
 └── Footer

Breaking a large interface into meaningful components can make the code easier to understand and maintain.

3. Use Meaningful Component Names

Component names should describe what the component represents.

function StudentCard() {
    return <div>Student</div>;
}

function CourseList() {
    return <div>Courses</div>;
}

Names such as StudentCard and CourseList are easier to understand than unclear names such as Box1 or Component2.

4. Keep Components Small and Understandable

Very large components can become difficult to read and maintain.

Instead of putting an entire application page into one component, separate meaningful sections into components.

function Dashboard() {

    return (
        <>

            <Header />
            <Statistics />
            <RecentOrders />
            <Footer />

        </>
    );
}

5. Keep State as Local as Practical

State should generally live in the component or the nearest appropriate common parent that needs it.

function SearchBox() {

    const [search, setSearch] =
        useState("");

    return (
        <input
            value={search}
            onChange={e =>
                setSearch(
                    e.target.value
                )
            }
        />
    );
}

Do not move every piece of state into a global store if only one component needs it.

6. Avoid Duplicated State

Avoid storing information that can easily be calculated from existing state.

const fullName =
    firstName + " " + lastName;

There is usually no need to create separate state for fullName when it can be derived from firstName and lastName.

7. Use Props for Data Flow

Props provide a clear way to pass data from a parent component to a child component.

function StudentCard({
    name,
    course
}) {

    return (
        <div>

            <h3>
                {name}
            </h3>

            <p>
                {course}
            </p>

        </div>
    );
}

8. Use Destructuring for Props

Destructuring can make component parameters easier to read.

function CourseCard({
    title,
    fee,
    duration
}) {

    return (
        <div>

            <h2>
                {title}
            </h2>

            <p>
                Fee: {fee}
            </p>

            <p>
                Duration: {duration}
            </p>

        </div>
    );
}

9. Use Keys Correctly

When rendering a list, provide a stable key for each item.

{students.map(student => (

    <StudentCard
        key={student.id}
        student={student}
    />

))}

A stable identifier is generally preferable to generating a new random key during every render.

10. Keep JSX Readable

Readable JSX is easier to understand and debug.

function Profile() {

    return (
        <div className="profile">

            <h1>
                Student Profile
            </h1>

            <p>
                Welcome to the dashboard.
            </p>

        </div>
    );
}

Use indentation and meaningful structure rather than putting a large amount of JSX on one line.

11. Avoid Unnecessary useEffect

Use useEffect for side effects and synchronization with external systems. Do not use it simply to calculate values that can be calculated during rendering.

const total =
    price * quantity;

A simple derived value usually does not need an effect.

12. Keep Effects Focused

An effect should have a clear purpose.

useEffect(() => {

    document.title =
        "React Course";

}, []);

Avoid placing unrelated operations into one large effect when separate effects would make the logic clearer.

13. Handle Loading States

Applications that load data should clearly represent the loading state.

if (loading) {

    return (
        <p>
            Loading...
        </p>
    );
}

This provides feedback while asynchronous work is in progress.

14. Handle Error States

API requests and other asynchronous operations can fail. Handle errors instead of assuming every request succeeds.

if (error) {

    return (
        <div className="alert alert-danger">

            {error}

        </div>
    );
}

15. Keep API Logic Organized

For larger applications, keeping API-related code organized can make components easier to maintain.

src/
├── components/
├── pages/
├── services/
│   └── api.js
├── hooks/
└── App.jsx

A service layer or API helper can centralize repeated request logic.

16. Create Custom Hooks for Repeated Logic

Custom Hooks can extract reusable stateful logic.

function useCounter() {

    const [count, setCount] =
        useState(0);

    function increment() {
        setCount(
            value => value + 1
        );
    }

    return {
        count,
        increment
    };
}

17. Use Context Carefully

Context is useful for values that need to be shared across many components.

Examples include:

  • Theme
  • Current user
  • Language settings
  • Authentication state

Do not automatically put every piece of application state into Context.

18. Keep Event Handlers Clear

Give meaningful names to event handler functions.

function handleSubmit(e) {

    e.preventDefault();

    // Submit form

}

return (
    <form
        onSubmit={handleSubmit}
    >

        ...

    </form>
);

19. Validate Forms Properly

Validate user input before submitting forms and provide useful feedback.

if (!email) {

    setError(
        "Email is required"
    );

    return;
}

Important validation should also be performed by the backend because frontend validation alone is not a security boundary.

20. Keep Authentication Logic Centralized

Authentication state and operations can be organized in a dedicated authentication context or custom hook.

const {
    user,
    login,
    logout
} = useAuth();

This can prevent authentication logic from being duplicated across many components.

21. Protect Sensitive API Requests

Frontend route protection is not enough to secure sensitive data.

React UI
   ↓
Protected Route
   ↓
API Request
   ↓
Backend Authentication
   ↓
Backend Authorization
   ↓
Protected Data

The server should verify that the user is allowed to perform the requested operation.

22. Optimize Only When Needed

Do not add useMemo, useCallback, or React.memo everywhere without a reason.

Measure
   ↓
Find Problem
   ↓
Optimize
   ↓
Measure Again

Performance optimization should solve a measured or clearly understood problem.

23. Use Code Splitting for Large Applications

Large applications can split code so that features are loaded when needed.

const Admin =
    lazy(() =>
        import("./Admin")
    );

This can reduce the amount of JavaScript needed for the initial application load.

24. Optimize Large Lists

Large lists can require substantial rendering work.

Possible approaches include:

  • Pagination
  • Virtualization
  • Filtering data before rendering
  • Memoizing appropriate components
  • Loading data in smaller groups
Users
  ↓
Filter
  ↓
Paginate
  ↓
Render Required Items

25. Keep File and Folder Structure Organized

A consistent project structure helps developers locate code quickly.

src/
├── components/
├── pages/
├── hooks/
├── services/
├── context/
├── assets/
├── App.jsx
└── main.jsx

The exact structure can vary depending on the size and requirements of the application.

26. Use Environment Variables Carefully

Environment variables can be useful for configuration such as API base URLs.

const API_URL =
    import.meta.env.VITE_API_URL;
Important: Values exposed to frontend code should not be treated as secret. Do not put passwords, private keys, or other sensitive secrets into client-side environment variables.

27. Use Error Boundaries for UI Errors

Error boundaries can provide fallback UI when a descendant component throws a rendering error.

Application
     ↓
Error Boundary
     ↓
React Components
     ↓
Error
     ↓
Fallback UI

Error boundaries are useful for preventing one rendering failure from producing an uncontrolled user experience across a larger interface.

28. Complete Best-Practice Component Example

import {
    useEffect,
    useState
} from "react";

function StudentList() {

    const [students, setStudents] =
        useState([]);

    const [loading, setLoading] =
        useState(true);

    const [error, setError] =
        useState("");

    useEffect(() => {

        async function loadStudents() {

            try {

                const response =
                    await fetch(
                        "/api/students"
                    );

                if (!response.ok) {
                    throw new Error(
                        "Failed to load students"
                    );
                }

                const data =
                    await response.json();

                setStudents(data);

            } catch (err) {

                setError(
                    err.message
                );

            } finally {

                setLoading(false);

            }
        }

        loadStudents();

    }, []);

    if (loading) {

        return (
            <p>
                Loading students...
            </p>
        );
    }

    if (error) {

        return (
            <div className="alert alert-danger">
                {error}
            </div>
        );
    }

    return (
        <div>

            <h2>
                Students
            </h2>

            {students.map(
                student => (

                <div
                    key={student.id}
                    className="card mb-2"
                >

                    <div className="card-body">

                        <h5>
                            {student.name}
                        </h5>

                    </div>

                </div>

            ))}

        </div>
    );
}

export default StudentList;

29. React Development Checklist

  • Are components reusable?
  • Does each component have a clear responsibility?
  • Is state kept in an appropriate location?
  • Is duplicated state avoided?
  • Are list keys stable?
  • Are loading and error states handled?
  • Are API calls organized?
  • Are unnecessary effects avoided?
  • Are sensitive operations protected on the backend?
  • Has performance been measured before optimization?

30. React Best Practices Summary

  • Build reusable and focused components.
  • Use meaningful names for components, variables, and functions.
  • Keep state as local as practical.
  • Avoid unnecessary duplicated state.
  • Use props for clear parent-to-child data flow.
  • Use stable keys when rendering lists.
  • Keep effects focused on actual side effects.
  • Handle loading and error states.
  • Organize API, hooks, context, and components clearly.
  • Optimize performance only after identifying a real need.
  • Protect sensitive operations on the backend.
  • Keep security and maintainability in mind while building React applications.
Clean Components
      ↓
Clear Data Flow
      ↓
Organized Logic
      ↓
Reliable Application
      ↓
Maintainable React Project

📌 Key Points

  • Use reusable components with clear responsibilities.
  • Keep state as local as practical.
  • Avoid unnecessary duplicated state and effects.
  • Use props and Context appropriately for data sharing.
  • Use stable keys when rendering lists.
  • Handle loading, errors, and empty states properly.
  • Organize API calls and reusable logic into appropriate modules or hooks.
  • Protect sensitive data and operations on the backend.
  • Keep frontend environment variables free of secrets.
  • Measure performance before applying advanced optimization techniques.

🧠 Quick Quiz

Question: Which is a good React best practice?