<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Untitled Publication]]></title><description><![CDATA[Untitled Publication]]></description><link>https://ngreddy.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Fri, 13 Nov 2026 19:24:31 GMT</lastBuildDate><atom:link href="https://ngreddy.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[React Development Best Practices]]></title><description><![CDATA[Purpose: A practical reference for building React applications that are easy to understand, test, maintain, and scale. The best React code is not the code…]]></description><link>https://ngreddy.hashnode.dev/react-development-best-practices</link><guid isPermaLink="true">https://ngreddy.hashnode.dev/react-development-best-practices</guid><dc:creator><![CDATA[Gangadhar Reddy]]></dc:creator><pubDate>Thu, 12 Nov 2026 09:27:41 GMT</pubDate><content:encoded><![CDATA[<h2>A practical guide with good and bad examples</h2>
<p><strong>Purpose:</strong> A practical reference for building React applications that are easy to understand, test, maintain, and scale.</p>
<p><strong>The best React code is not the code that uses the most Hooks or libraries. It is code that solves the requirement clearly, safely, and with the least unnecessary complexity.</strong></p>
<p><strong>Audience:</strong> Beginner-to-intermediate React developers and teams reviewing production code.</p>
<p><strong>Examples:</strong> Most examples use modern function components, JavaScript, and Vite-style environment variables. Add imports where needed. Adapt the folder structure to the size of your application rather than following it mechanically.</p>
<hr />
<h2>Contents</h2>
<ol>
<li><a href="#1-core-principles">Core principles</a></li>
<li><a href="#2-component-design">Component design</a></li>
<li><a href="#3-file-naming-and-folder-structure">File naming and folder structure</a></li>
<li><a href="#4-props-and-component-communication">Props and component communication</a></li>
<li><a href="#5-state-management-fundamentals">State management fundamentals</a></li>
<li><a href="#6-usestate-best-practices">useState best practices</a></li>
<li><a href="#7-useeffect-best-practices">useEffect best practices</a></li>
<li><a href="#8-other-important-hooks">Other important Hooks</a></li>
<li><a href="#9-custom-hooks">Custom Hooks</a></li>
<li><a href="#10-context-api-and-usecontext">Context API and useContext</a></li>
<li><a href="#11-usereducer">useReducer</a></li>
<li><a href="#12-choosing-usestate-usereducer-context-or-redux-toolkit">Choosing useState, useReducer, Context, or Redux Toolkit</a></li>
<li><a href="#13-redux-toolkit">Redux Toolkit</a></li>
<li><a href="#14-api-calls-and-data-fetching">API calls and data fetching</a></li>
<li><a href="#15-loading-error-empty-and-success-states">Loading, error, empty, and success states</a></li>
<li><a href="#16-forms-and-validation">Forms and validation</a></li>
<li><a href="#17-rendering-and-performance">Rendering and performance</a></li>
<li><a href="#18-routing-and-code-splitting">Routing and code splitting</a></li>
<li><a href="#19-error-boundaries-and-error-handling">Error boundaries and error handling</a></li>
<li><a href="#20-security-and-accessibility">Security and accessibility</a></li>
<li><a href="#21-testing">Testing</a></li>
<li><a href="#22-code-quality-and-maintainability">Code quality and maintainability</a></li>
<li><a href="#23-example-feature-structure">Example feature structure</a></li>
<li><a href="#24-code-review-checklist">Code review checklist</a></li>
<li><a href="#25-useful-official-resources">Useful official resources</a></li>
</ol>
<hr />
<h2>1. Core principles</h2>
<p>Keep these principles in mind when writing React code:</p>
<ul>
<li><strong>Keep components focused:</strong> A component should have one clear UI responsibility.</li>
<li><strong>Keep state close to where it is used:</strong> Do not make every value global.</li>
<li><strong>Avoid duplicated state:</strong> Derive values from existing state when possible.</li>
<li><strong>Do not mutate state:</strong> Create new arrays and objects when updating state.</li>
<li><strong>Separate responsibilities:</strong> UI, API communication, business logic, and shared utilities should not become one large file.</li>
<li><strong>Handle every request state:</strong> Consider loading, success, empty, and error states.</li>
<li><strong>Optimize based on evidence:</strong> Do not add memoization everywhere.</li>
<li><strong>Make the UI accessible:</strong> Prefer semantic HTML and keyboard-friendly interactions.</li>
<li><strong>Test important behaviour:</strong> Focus on user-visible outcomes and critical business rules.</li>
</ul>
<p>React is declarative: describe what the UI should look like for the current state. Avoid manually changing the DOM when React state can represent the change.</p>
<h3>Bad: manually changing the DOM</h3>
<pre><code class="language-jsx">function Message() {
  function showMessage() {
    document.getElementById("message").innerText = "Saved";
  }

  return &lt;p id="message"&gt;Not saved&lt;/p&gt;;
}
</code></pre>
<h3>Good: represent the UI with state</h3>
<pre><code class="language-jsx">import { useState } from "react";

function Message() {
  const [saved, setSaved] = useState(false);

  return (
    &lt;&gt;
      &lt;p&gt;{saved ? "Saved" : "Not saved"}&lt;/p&gt;
      &lt;button onClick={() =&gt; setSaved(true)}&gt;Save&lt;/button&gt;
    &lt;/&gt;
  );
}
</code></pre>
<hr />
<h2>2. Component design</h2>
<h3>2.1 Keep components small and focused</h3>
<p>A component should have a clear purpose. Split a large component when it handles unrelated UI, data fetching, form logic, filtering, and modal behaviour all together.</p>
<h3>Bad</h3>
<pre><code class="language-jsx">function UsersPage() {
  // Fetch users
  // Filter users
  // Handle form submission
  // Manage modal
  // Render header, filters, table and modal
  // Hundreds of lines of mixed responsibilities
}
</code></pre>
<h3>Good</h3>
<pre><code class="language-jsx">function UsersPage() {
  return (
    &lt;PageLayout&gt;
      &lt;UsersHeader /&gt;
      &lt;UserFilters /&gt;
      &lt;UsersTable /&gt;
      &lt;UserFormDialog /&gt;
    &lt;/PageLayout&gt;
  );
}
</code></pre>
<p>Do not split every few lines into a new component. Extract a component when it has a meaningful responsibility, is reused, or makes the parent easier to understand.</p>
<h3>2.2 Prefer function components</h3>
<p>Use function components and Hooks for new React code.</p>
<pre><code class="language-jsx">function Welcome({ name }) {
  return &lt;h1&gt;Welcome, {name}&lt;/h1&gt;;
}
</code></pre>
<p>Class components are still supported, and you may encounter them in older projects. New application code generally uses function components.</p>
<h3>2.3 Keep render logic readable</h3>
<h3>Bad</h3>
<pre><code class="language-jsx">return (
  &lt;div&gt;
    {users &amp;&amp; users.length &gt; 0
      ? users.map((user) =&gt; user.active
          ? &lt;div&gt;{user.name}&lt;/div&gt;
          : null)
      : &lt;p&gt;No users&lt;/p&gt;}
  &lt;/div&gt;
);
</code></pre>
<h3>Good</h3>
<pre><code class="language-jsx">if (users.length === 0) {
  return &lt;p&gt;No users found.&lt;/p&gt;;
}

return (
  &lt;ul&gt;
    {users.map((user) =&gt; (
      &lt;li key={user.id}&gt;
        {user.name}
      &lt;/li&gt;
    ))}
  &lt;/ul&gt;
);
</code></pre>
<p>Use helper functions or smaller components when conditional rendering becomes complicated.</p>
<h3>2.4 Avoid defining components inside components</h3>
<p>Defining a component inside another component creates a new component type on every parent render and can reset child state.</p>
<h3>Avoid</h3>
<pre><code class="language-jsx">function Dashboard() {
  function ProfileCard() {
    return &lt;div&gt;Profile&lt;/div&gt;;
  }

  return &lt;ProfileCard /&gt;;
}
</code></pre>
<h3>Prefer</h3>
<pre><code class="language-jsx">function ProfileCard() {
  return &lt;div&gt;Profile&lt;/div&gt;;
}

function Dashboard() {
  return &lt;ProfileCard /&gt;;
}
</code></pre>
<hr />
<h2>3. File naming and folder structure</h2>
<p>Consistency matters more than one universally correct naming scheme. Agree on conventions and use them throughout the project.</p>
<h3>3.1 Recommended file naming</h3>
<hr />
<p>Item Recommended convention Example</p>
<hr />
<p>React component PascalCase <code>UserProfile.jsx</code></p>
<p>Page component PascalCase <code>UsersPage.jsx</code></p>
<p>Custom Hook camelCase with <code>use</code> <code>useUsers.js</code> prefix</p>
<p>Utility function camelCase <code>formatDate.js</code></p>
<p>API/service module camelCase <code>userService.js</code></p>
<p>Context PascalCase or <code>AuthContext.jsx</code> descriptive camelCase</p>
<p>Reducer camelCase <code>cartReducer.js</code></p>
<p>Constants camelCase file; <code>appConstants.js</code> uppercase values where<br />useful</p>
<p>Test Match source file plus <code>UserProfile.test.jsx</code> test suffix</p>
<h2>Styles Match component name <code>UserProfile.module.css</code></h2>
<p>Use names that describe purpose. Avoid vague names such as <code>data.js</code>, <code>common2.js</code>, <code>helper.js</code>, or <code>NewComponent.jsx</code>.</p>
<h3>3.2 Small application structure</h3>
<p>For a small app, a simple structure is enough:</p>
<pre><code class="language-text">src/
├── assets/
├── components/
├── pages/
├── hooks/
├── services/
├── utils/
├── App.jsx
└── main.jsx
</code></pre>
<h3>3.3 Feature-based structure for medium and large applications</h3>
<p>Group code by feature so related UI, hooks, API functions, and tests stay together.</p>
<pre><code class="language-text">src/
├── app/
│   ├── App.jsx
│   ├── routes.jsx
│   └── store.js
│
├── features/
│   ├── auth/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── services/
│   │   ├── authSlice.js
│   │   └── AuthPage.jsx
│   │
│   ├── users/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── services/
│   │   ├── usersSlice.js
│   │   └── UsersPage.jsx
│   │
│   └── products/
│       ├── components/
│       ├── hooks/
│       ├── services/
│       └── ProductsPage.jsx
│
├── shared/
│   ├── components/
│   ├── hooks/
│   └── utils/
│
├── services/
│   └── apiClient.js
│
├── assets/
├── styles/
└── main.jsx
</code></pre>
<p>Use <code>features/</code> for feature-specific code and <code>shared/</code> for genuinely reusable code. Do not move a component into <code>shared/</code> just because it is used by two files if it is still tightly coupled to one feature.</p>
<h3>3.4 Avoid overengineering folders</h3>
<p>Do not create many empty folders for a small project. Start simple and introduce feature boundaries as the application grows.</p>
<hr />
<h2>4. Props and component communication</h2>
<p>Props are the normal way for a parent to pass data and callbacks to a child.</p>
<h3>Good</h3>
<pre><code class="language-jsx">function UserCard({ user, onSelect }) {
  return (
    &lt;button onClick={() =&gt; onSelect(user.id)}&gt;
      {user.name}
    &lt;/button&gt;
  );
}

function UsersList({ users }) {
  function handleSelect(userId) {
    console.log("Selected:", userId);
  }

  return users.map((user) =&gt; (
    &lt;UserCard
      key={user.id}
      user={user}
      onSelect={handleSelect}
    /&gt;
  ));
}
</code></pre>
<h3>Avoid unnecessary prop drilling</h3>
<p>Passing a value through many components that do not use it can make the component tree difficult to maintain.</p>
<p>Before introducing global state, consider: - Moving the state closer to the components that need it. - Passing a value through a small number of component levels. - Using component composition. - Using Context for genuinely shared values. - Using Redux Toolkit when the application needs a central client-state store.</p>
<h3>Avoid passing huge objects when a component needs only a few values</h3>
<p>Prefer a focused interface:</p>
<pre><code class="language-jsx">&lt;UserAvatar
  name={user.name}
  imageUrl={user.avatarUrl}
/&gt;
</code></pre>
<p>rather than passing a large object if the component only needs two fields. Passing an object is still appropriate when the component genuinely represents that object.</p>
<hr />
<h2>5. State management fundamentals</h2>
<p>React state is a snapshot for a particular render. Calling a setter requests another render; it does not change the value in the currently running function.</p>
<h3>5.1 Keep state minimal</h3>
<h3>Bad: store values that can be calculated</h3>
<pre><code class="language-jsx">const [price, setPrice] = useState(100);
const [quantity, setQuantity] = useState(2);
const [total, setTotal] = useState(200);
</code></pre>
<p>Now every price or quantity update must also keep <code>total</code> in sync.</p>
<h3>Good: derive the value</h3>
<pre><code class="language-jsx">const [price, setPrice] = useState(100);
const [quantity, setQuantity] = useState(2);

const total = price * quantity;
</code></pre>
<h3>5.2 Never mutate state directly</h3>
<h3>Bad</h3>
<pre><code class="language-jsx">user.name = "Asha";
setUser(user);

items.push(newItem);
setItems(items);
</code></pre>
<p>React may not detect a changed reference, and mutation can cause difficult-to-debug bugs.</p>
<h3>Good</h3>
<pre><code class="language-jsx">setUser((previousUser) =&gt; ({
  ...previousUser,
  name: "Asha",
}));

setItems((previousItems) =&gt; [
  ...previousItems,
  newItem,
]);
</code></pre>
<p>For an array item update:</p>
<pre><code class="language-jsx">setItems((previousItems) =&gt;
  previousItems.map((item) =&gt;
    item.id === updatedItem.id ? updatedItem : item
  )
);
</code></pre>
<p>For deletion:</p>
<pre><code class="language-jsx">setItems((previousItems) =&gt;
  previousItems.filter((item) =&gt; item.id !== itemId)
);
</code></pre>
<h3>5.3 Do not store values in state if they are not part of the UI</h3>
<p>Use a normal variable for a value that does not need to trigger a render. Use <code>useRef</code> for mutable values that must persist between renders without triggering a render.</p>
<hr />
<h2>6. useState best practices</h2>
<p>Use <code>useState</code> for simple component state such as input values, open/closed flags, selected tabs, and small independent values.</p>
<h3>6.1 Basic usage</h3>
<pre><code class="language-jsx">import { useState } from "react";

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

  return (
    &lt;button onClick={() =&gt; setCount(count + 1)}&gt;
      Count: {count}
    &lt;/button&gt;
  );
}
</code></pre>
<h3>6.2 Use functional updates when the next value depends on the previous value</h3>
<h3>Risky when multiple updates use the same render snapshot</h3>
<pre><code class="language-jsx">function incrementThreeTimes() {
  setCount(count + 1);
  setCount(count + 1);
  setCount(count + 1);
}
</code></pre>
<p>Each call reads the same <code>count</code> snapshot.</p>
<h3>Good</h3>
<pre><code class="language-jsx">function incrementThreeTimes() {
  setCount((previous) =&gt; previous + 1);
  setCount((previous) =&gt; previous + 1);
  setCount((previous) =&gt; previous + 1);
}
</code></pre>
<h3>6.3 Lazy initialization for expensive initial calculations</h3>
<pre><code class="language-jsx">const [settings, setSettings] = useState(() =&gt; {
  return loadInitialSettings();
});
</code></pre>
<p>The initializer is used to calculate the initial state. Do not pass <code>loadInitialSettings()</code> if you do not want it evaluated on every render.</p>
<h3>6.4 Group related state when it makes updates clearer</h3>
<p>A few related fields can be stored in one object:</p>
<pre><code class="language-jsx">const [form, setForm] = useState({
  name: "",
  email: "",
});

function updateField(field, value) {
  setForm((previous) =&gt; ({
    ...previous,
    [field]: value,
  }));
}
</code></pre>
<p>Do not combine unrelated values into one giant state object just to reduce the number of Hooks.</p>
<hr />
<h2>7. useEffect best practices</h2>
<p><code>useEffect</code> is for synchronizing a component with something outside React, such as a network connection, browser event, timer, or third-party widget.</p>
<p>It is not a general-purpose place to calculate values or respond to every user action.</p>
<h3>7.1 Do not use an Effect for derived values</h3>
<h3>Bad</h3>
<pre><code class="language-jsx">const [firstName, setFirstName] = useState("Maya");
const [lastName, setLastName] = useState("Rao");
const [fullName, setFullName] = useState("");

useEffect(() =&gt; {
  setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);
</code></pre>
<h3>Good</h3>
<pre><code class="language-jsx">const fullName = `${firstName} ${lastName}`;
</code></pre>
<h3>7.2 Do not use an Effect for a user event</h3>
<h3>Bad</h3>
<pre><code class="language-jsx">const [shouldSave, setShouldSave] = useState(false);

useEffect(() =&gt; {
  if (shouldSave) {
    saveUser();
  }
}, [shouldSave]);
</code></pre>
<h3>Good</h3>
<pre><code class="language-jsx">function handleSave() {
  saveUser();
}
</code></pre>
<p>The save is caused by a button click, so handle it in the event handler.</p>
<h3>7.3 Correct dependency handling</h3>
<p>Include every reactive value used inside the Effect. Do not silence dependency warnings just to make an Effect run less often. Restructure the code if the dependencies are causing an unwanted loop.</p>
<pre><code class="language-jsx">useEffect(() =&gt; {
  document.title = `Users (${users.length})`;
}, [users.length]);
</code></pre>
<h3>7.4 Clean up subscriptions and browser events</h3>
<pre><code class="language-jsx">useEffect(() =&gt; {
  function handleResize() {
    console.log(window.innerWidth);
  }

  window.addEventListener("resize", handleResize);

  return () =&gt; {
    window.removeEventListener("resize", handleResize);
  };
}, []);
</code></pre>
<h3>7.5 Cancel or ignore outdated requests</h3>
<pre><code class="language-jsx">useEffect(() =&gt; {
  const controller = new AbortController();

  async function loadUser() {
    try {
      const response = await fetch(`/api/users/${userId}`, {
        signal: controller.signal,
      });

      if (!response.ok) {
        throw new Error("Could not load user");
      }

      const data = await response.json();
      setUser(data);
    } catch (error) {
      if (error.name !== "AbortError") {
        setError(error.message);
      }
    }
  }

  loadUser();

  return () =&gt; controller.abort();
}, [userId]);
</code></pre>
<p>This prevents an obsolete request from updating the component after the <code>userId</code> changes or the component unmounts.</p>
<h3>7.6 Avoid Effects that update their own dependencies without a clear reason</h3>
<p>An Effect that updates a value it depends on can create an infinite render loop. Check whether the update belongs in an event handler or can be calculated during render.</p>
<hr />
<h2>8. Other important Hooks</h2>
<h3>8.1 useRef</h3>
<p>Use <code>useRef</code> for a DOM reference or a mutable value that should persist across renders but should not itself trigger a render.</p>
<p>DOM example:</p>
<pre><code class="language-jsx">import { useRef } from "react";

function SearchBox() {
  const inputRef = useRef(null);

  function focusInput() {
    inputRef.current?.focus();
  }

  return (
    &lt;&gt;
      &lt;input ref={inputRef} /&gt;
      &lt;button onClick={focusInput}&gt;Focus&lt;/button&gt;
    &lt;/&gt;
  );
}
</code></pre>
<p>Mutable value example:</p>
<pre><code class="language-jsx">const requestCount = useRef(0);
requestCount.current += 1;
</code></pre>
<p>Do not use a ref for data that must immediately appear in the UI. Use state for that.</p>
<h3>8.2 useMemo</h3>
<p>Use <code>useMemo</code> to cache the result of a costly calculation between renders when its dependencies have not changed.</p>
<pre><code class="language-jsx">const filteredUsers = useMemo(() =&gt; {
  return users.filter((user) =&gt;
    user.name.toLowerCase().includes(search.toLowerCase())
  );
}, [users, search]);
</code></pre>
<p>For a small array, calculate it directly:</p>
<pre><code class="language-jsx">const filteredUsers = users.filter((user) =&gt;
  user.name.toLowerCase().includes(search.toLowerCase())
);
</code></pre>
<p>Do not use <code>useMemo</code> by default for every variable.</p>
<h3>8.3 useCallback</h3>
<p>Use <code>useCallback</code> when a stable function reference is useful, for example when passing a callback to a memoized child or using it as a dependency of another Hook.</p>
<pre><code class="language-jsx">const handleSelect = useCallback((id) =&gt; {
  setSelectedId(id);
}, []);
</code></pre>
<p>Do not wrap every event handler in <code>useCallback</code>. It adds complexity and does not automatically make the application faster.</p>
<h3>8.4 React.memo</h3>
<p><code>React.memo</code> can skip a child render when its props have not changed.</p>
<pre><code class="language-jsx">const UserCard = memo(function UserCard({ user, onSelect }) {
  return (
    &lt;button onClick={() =&gt; onSelect(user.id)}&gt;
      {user.name}
    &lt;/button&gt;
  );
});
</code></pre>
<p>It is most useful when the component renders often, rendering is expensive, and props are stable. It is not a substitute for good state placement.</p>
<h3>8.5 useId</h3>
<p>Use <code>useId</code> to create stable IDs for accessibility relationships, such as connecting a label and input. Do not use it for list keys.</p>
<pre><code class="language-jsx">const inputId = useId();

return (
  &lt;&gt;
    &lt;label htmlFor={inputId}&gt;Email&lt;/label&gt;
    &lt;input id={inputId} type="email" /&gt;
  &lt;/&gt;
);
</code></pre>
<h3>8.6 useLayoutEffect</h3>
<p>Use <code>useLayoutEffect</code> only when you must measure or adjust layout before the browser paints, such as measuring an element to position a tooltip. Prefer <code>useEffect</code> for normal synchronization.</p>
<h3>8.7 Rules of Hooks</h3>
<ul>
<li>Call Hooks at the top level of a function component or custom Hook.</li>
<li>Do not call Hooks inside conditions, loops, callbacks, or regular utility functions.</li>
<li>Call Hooks in the same order on every render.</li>
<li>Use the <code>use</code> prefix for custom Hooks.</li>
<li>Follow the React Hooks ESLint rules.</li>
</ul>
<h3>Bad</h3>
<pre><code class="language-jsx">if (isLoggedIn) {
  const [user, setUser] = useState(null);
}
</code></pre>
<h3>Good</h3>
<pre><code class="language-jsx">const [user, setUser] = useState(null);

if (!isLoggedIn) {
  return &lt;Login /&gt;;
}
</code></pre>
<hr />
<h2>9. Custom Hooks</h2>
<p>Create a custom Hook when logic needs to be reused or when extracting it makes a component easier to read. A custom Hook shares logic, not one shared state instance: each call has its own state unless it uses a shared store or Context.</p>
<h3>Example: useWindowWidth</h3>
<pre><code class="language-jsx">import { useEffect, useState } from "react";

export function useWindowWidth() {
  const [width, setWidth] = useState(() =&gt; window.innerWidth);

  useEffect(() =&gt; {
    function handleResize() {
      setWidth(window.innerWidth);
    }

    window.addEventListener("resize", handleResize);
    return () =&gt; window.removeEventListener("resize", handleResize);
  }, []);

  return width;
}
</code></pre>
<p>Usage:</p>
<pre><code class="language-jsx">function ResponsiveMessage() {
  const width = useWindowWidth();

  return &lt;p&gt;Window width: {width}px&lt;/p&gt;;
}
</code></pre>
<p>For server-rendered applications, account for the fact that <code>window</code> is not available during server rendering. Initialize safely or use an appropriate client-only approach.</p>
<h3>Bad: custom Hook that only renames a built-in Hook</h3>
<pre><code class="language-jsx">function useMyState(initialValue) {
  return useState(initialValue);
}
</code></pre>
<p>This adds no meaningful abstraction. Create custom Hooks to encapsulate useful behaviour, not merely to wrap one line.</p>
<hr />
<h2>10. Context API and useContext</h2>
<p>Context lets a parent provide a value to many descendants without manually passing it through every intermediate component.</p>
<p>Good use cases: - Current authenticated user and authentication actions. - Theme. - Locale or language. - A small, stable application-wide setting.</p>
<p>Context is not automatically a replacement for Redux or a server-state library.</p>
<h3>10.1 Create a Context</h3>
<pre><code class="language-jsx">// context/ThemeContext.jsx
import { createContext, useContext, useState } from "react";

const ThemeContext = createContext(null);

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState("light");

  function toggleTheme() {
    setTheme((current) =&gt;
      current === "light" ? "dark" : "light"
    );
  }

  return (
    &lt;ThemeContext.Provider value={{ theme, toggleTheme }}&gt;
      {children}
    &lt;/ThemeContext.Provider&gt;
  );
}

export function useTheme() {
  const context = useContext(ThemeContext);

  if (context === null) {
    throw new Error("useTheme must be used inside ThemeProvider");
  }

  return context;
}
</code></pre>
<h3>10.2 Wrap the application</h3>
<pre><code class="language-jsx">// main.jsx
import { createRoot } from "react-dom/client";
import App from "./App";
import { ThemeProvider } from "./context/ThemeContext";

createRoot(document.getElementById("root")).render(
  &lt;ThemeProvider&gt;
    &lt;App /&gt;
  &lt;/ThemeProvider&gt;
);
</code></pre>
<h3>10.3 Consume Context</h3>
<pre><code class="language-jsx">import { useTheme } from "../context/ThemeContext";

function ThemeButton() {
  const { theme, toggleTheme } = useTheme();

  return (
    &lt;button onClick={toggleTheme}&gt;
      Current theme: {theme}
    &lt;/button&gt;
  );
}
</code></pre>
<h3>10.4 Bad Context usage</h3>
<p>Do not put every changing value in one giant Context:</p>
<pre><code class="language-jsx">&lt;AppContext.Provider
  value={{
    user,
    theme,
    cart,
    filters,
    modal,
    notifications,
    formValues,
  }}
&gt;
  {children}
&lt;/AppContext.Provider&gt;
</code></pre>
<p>This makes the provider difficult to maintain. Consumers can re-render when the provider value changes, even if they only need one part of it.</p>
<h3>Better</h3>
<ul>
<li>Split Contexts by responsibility, such as <code>AuthContext</code> and <code>ThemeContext</code>.</li>
<li>Keep frequently changing local state inside the feature that owns it.</li>
<li>Memoize provider values only when it helps and dependencies are correct.</li>
<li>Consider Redux Toolkit for complex shared client state.</li>
</ul>
<h3>10.5 When should you use Context?</h3>
<p>Use Context when: - Many descendants need the same value. - The value is conceptually application-wide or subtree-wide. - Prop drilling is making the component tree harder to maintain. - The update frequency and complexity are manageable.</p>
<p>Avoid Context when: - Only one or two nearby components need the value. - The value changes very frequently and causes broad re-rendering. - You need advanced store features, middleware, DevTools workflows, or complex update patterns.</p>
<hr />
<h2>11. useReducer</h2>
<p><code>useReducer</code> is a Hook for managing state through actions. It is useful when state transitions are more involved than a few independent setter calls.</p>
<h3>11.1 Basic structure</h3>
<p>A reducer receives the current state and an action, then returns the next state.</p>
<pre><code class="language-jsx">function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 };

    case "decrement":
      return { count: state.count - 1 };

    case "reset":
      return { count: 0 };

    default:
      throw new Error(`Unknown action: ${action.type}`);
  }
}
</code></pre>
<p>Use it in a component:</p>
<pre><code class="language-jsx">import { useReducer } from "react";

const initialState = { count: 0 };

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    &lt;&gt;
      &lt;h2&gt;{state.count}&lt;/h2&gt;

      &lt;button onClick={() =&gt; dispatch({ type: "increment" })}&gt;
        +
      &lt;/button&gt;

      &lt;button onClick={() =&gt; dispatch({ type: "decrement" })}&gt;
        -
      &lt;/button&gt;

      &lt;button onClick={() =&gt; dispatch({ type: "reset" })}&gt;
        Reset
      &lt;/button&gt;
    &lt;/&gt;
  );
}
</code></pre>
<h3>11.2 Practical example: form state</h3>
<p>When several related fields and transitions are involved, a reducer can make the update rules explicit.</p>
<pre><code class="language-jsx">const initialState = {
  name: "",
  email: "",
  errors: {},
  status: "idle",
};

function formReducer(state, action) {
  switch (action.type) {
    case "fieldChanged":
      return {
        ...state,
        [action.field]: action.value,
        errors: {
          ...state.errors,
          [action.field]: "",
        },
      };

    case "validationFailed":
      return {
        ...state,
        errors: action.errors,
        status: "idle",
      };

    case "submitting":
      return { ...state, status: "loading" };

    case "submitted":
      return { ...state, status: "success" };

    case "failed":
      return { ...state, status: "error" };

    default:
      throw new Error(`Unknown action: ${action.type}`);
  }
}
</code></pre>
<pre><code class="language-jsx">function RegistrationForm() {
  const [state, dispatch] = useReducer(formReducer, initialState);

  function handleChange(event) {
    const { name, value } = event.target;

    dispatch({
      type: "fieldChanged",
      field: name,
      value,
    });
  }

  return (
    &lt;form&gt;
      &lt;input
        name="name"
        value={state.name}
        onChange={handleChange}
      /&gt;

      &lt;input
        name="email"
        value={state.email}
        onChange={handleChange}
      /&gt;

      &lt;p&gt;Status: {state.status}&lt;/p&gt;
    &lt;/form&gt;
  );
}
</code></pre>
<p>This is only the state-management part of a form; add validation, submission, and accessible labels for a production form.</p>
<h3>11.3 useReducer best practices</h3>
<ul>
<li>Keep reducers pure: no API calls, timers, random values, or DOM operations inside them.</li>
<li>Do not mutate the existing state.</li>
<li>Use descriptive action names, such as <code>itemAdded</code> or <code>requestFailed</code>.</li>
<li>Keep initial state in a clearly named constant.</li>
<li>Keep reducer logic outside the component when it is substantial.</li>
<li>Do not use a reducer for trivial state that is clearer with <code>useState</code>.</li>
</ul>
<h3>11.4 When should you use useReducer?</h3>
<p>Choose <code>useReducer</code> when: - Several state values change together. - Many event handlers update the same state object. - The next state depends on the current state and the action. - You want transitions to be centralized and easy to test. - A form, wizard, cart, or local workflow has multiple states and transitions.</p>
<p>Use <code>useState</code> when: - State is simple and independent. - A boolean, input value, selected tab, or small value is all you need. - A reducer would add more code than clarity.</p>
<hr />
<h2>12. Choosing useState, useReducer, Context, or Redux Toolkit</h2>
<p>These tools solve related but different problems. They are not mutually exclusive: for example, Context can provide a reducer's state and dispatch to descendants.</p>
<hr />
<p>Tool Best for Example</p>
<hr />
<p><code>useState</code> Simple local state Input value, modal open</p>
<p><code>useReducer</code> Related state with Multi-step form, local multiple transitions cart logic</p>
<p>Context + <code>useState</code> Shared, relatively Theme, current user simple state</p>
<p>Context + <code>useReducer</code> Shared state with Small workflow shared structured transitions by many descendants</p>
<p>Redux Toolkit Complex shared client Large app with shared state across features entities and workflows</p>
<h2>TanStack Query / RTK Server state and API Fetching, caching, Query caching refetching API data</h2>
<h3>Decision guide</h3>
<p><strong>Use</strong> <code>useState</code> if one component owns a simple value.</p>
<p><strong>Use</strong> <code>useReducer</code> if one feature owns a group of related values and has several meaningful transitions.</p>
<p><strong>Use Context</strong> if many descendants need the same value and passing props is becoming awkward.</p>
<p><strong>Use Context +</strong> <code>useReducer</code> if shared state has several related transitions but the app does not need a full Redux store.</p>
<p><strong>Use Redux Toolkit</strong> if many features need coordinated shared client state, predictable action-based updates, middleware, strong Redux DevTools support, or a standard store architecture.</p>
<p><strong>Use a server-state tool</strong> when the hard problem is fetching, caching, synchronizing, and invalidating remote data. Redux is not automatically the best place for all API response data.</p>
<h3>Example choices</h3>
<hr />
<p>Requirement Good starting point</p>
<hr />
<p>Open/close one dialog <code>useState</code></p>
<p>Filter a table on one page <code>useState</code></p>
<p>Multi-step checkout state inside <code>useReducer</code> one feature</p>
<p>Theme across the app Context</p>
<p>Authenticated user shared across Context or Redux Toolkit, depending many components on complexity</p>
<p>Shopping cart shared across many Redux Toolkit may be appropriate routes with complex actions</p>
<p>Cache product API results TanStack Query or RTK Query</p>
<h2>Server-side form validation API + form state/validation library</h2>
<p>Do not choose Redux just because the application has many components. Choose it when the state-sharing and update requirements justify a centralized store.</p>
<hr />
<h2>13. Redux Toolkit</h2>
<p>Redux Toolkit (RTK) is the recommended way to write Redux logic. It reduces boilerplate and provides a standard approach to reducers, actions, and store setup.</p>
<p>Use it for complex shared <strong>client state</strong>. For remote API data, consider RTK Query (included with Redux Toolkit) or another server-state library.</p>
<h3>13.1 Install</h3>
<pre><code class="language-bash">npm install @reduxjs/toolkit react-redux
</code></pre>
<h3>13.2 Create a slice</h3>
<pre><code class="language-jsx">// features/cart/cartSlice.js
import { createSlice } from "@reduxjs/toolkit";

const initialState = {
  items: [],
};

const cartSlice = createSlice({
  name: "cart",
  initialState,
  reducers: {
    itemAdded(state, action) {
      state.items.push(action.payload);
    },

    itemRemoved(state, action) {
      state.items = state.items.filter(
        (item) =&gt; item.id !== action.payload
      );
    },

    cartCleared(state) {
      state.items = [];
    },
  },
});

export const { itemAdded, itemRemoved, cartCleared } =
  cartSlice.actions;

export default cartSlice.reducer;
</code></pre>
<p>RTK uses Immer internally, so this reducer syntax is safe inside <code>createSlice</code>. Do not copy this mutation-style syntax into ordinary React state updates or plain reducers.</p>
<h3>13.3 Configure the store</h3>
<pre><code class="language-jsx">// app/store.js
import { configureStore } from "@reduxjs/toolkit";
import cartReducer from "../features/cart/cartSlice";

export const store = configureStore({
  reducer: {
    cart: cartReducer,
  },
});
</code></pre>
<h3>13.4 Add the Provider</h3>
<pre><code class="language-jsx">// main.jsx
import { Provider } from "react-redux";
import { store } from "./app/store";

createRoot(document.getElementById("root")).render(
  &lt;Provider store={store}&gt;
    &lt;App /&gt;
  &lt;/Provider&gt;
);
</code></pre>
<h3>13.5 Read state and dispatch actions</h3>
<pre><code class="language-jsx">import { useDispatch, useSelector } from "react-redux";
import { itemAdded } from "./cartSlice";

function Cart() {
  const items = useSelector((state) =&gt; state.cart.items);
  const dispatch = useDispatch();

  return (
    &lt;&gt;
      &lt;p&gt;Items: {items.length}&lt;/p&gt;
      &lt;button
        onClick={() =&gt;
          dispatch(itemAdded({ id: 1, name: "Keyboard" }))
        }
      &gt;
        Add item
      &lt;/button&gt;
    &lt;/&gt;
  );
}
</code></pre>
<h3>13.6 Redux Toolkit best practices</h3>
<ul>
<li>Create slices around features or meaningful state domains.</li>
<li>Keep reducers synchronous and pure in intent; use RTK's async patterns for asynchronous workflows.</li>
<li>Use selectors to read only the state a component needs.</li>
<li>Avoid copying the same data into multiple slices.</li>
<li>Do not put temporary input state in Redux without a good reason.</li>
<li>Keep non-serializable values such as DOM nodes, functions, and class instances out of the store.</li>
<li>Use Redux DevTools to inspect actions and state changes.</li>
<li>Consider RTK Query for API fetching and caching rather than manually building every request lifecycle.</li>
</ul>
<h3>13.7 When Redux Toolkit is unnecessary</h3>
<p>For a small app with a few local states, adding Redux creates extra setup without much benefit. Start with component state and Context where appropriate, then introduce Redux Toolkit when the actual requirements justify it.</p>
<hr />
<h2>14. API calls and data fetching</h2>
<p>Keep network communication out of large UI components. Put reusable request configuration in an API client and feature-specific endpoints in service modules.</p>
<h3>14.1 Example folder structure</h3>
<pre><code class="language-text">src/
├── services/
│   └── apiClient.js
└── features/
    └── users/
        ├── services/
        │   └── userService.js
        ├── hooks/
        │   └── useUsers.js
        └── UsersPage.jsx
</code></pre>
<h3>14.2 Centralize the API base URL</h3>
<p>For a Vite project:</p>
<pre><code class="language-env">VITE_API_BASE_URL=https://api.example.com
</code></pre>
<pre><code class="language-jsx">// services/apiClient.js
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;

export async function apiRequest(path, options = {}) {
  const response = await fetch(`${API_BASE_URL}${path}`, {
    ...options,
    headers: {
      "Content-Type": "application/json",
      ...options.headers,
    },
  });

  if (!response.ok) {
    const message = `Request failed: ${response.status}`;
    throw new Error(message);
  }

  if (response.status === 204) {
    return null;
  }

  return response.json();
}
</code></pre>
<p>This is a simple starting point. Production clients may also need authentication handling, structured error parsing, request IDs, timeouts, and special handling for file uploads. Do not set <code>Content-Type: application/json</code> for <code>FormData</code>; let the browser set its multipart boundary.</p>
<h3>14.3 Create feature-specific API functions</h3>
<pre><code class="language-jsx">// features/users/services/userService.js
import { apiRequest } from "../../../services/apiClient";

export function getUsers({ signal } = {}) {
  return apiRequest("/users", { signal });
}

export function getUserById(id, { signal } = {}) {
  return apiRequest(`/users/${id}`, { signal });
}

export function createUser(payload) {
  return apiRequest("/users", {
    method: "POST",
    body: JSON.stringify(payload),
  });
}
</code></pre>
<h3>14.4 Bad: API calls scattered through UI components</h3>
<pre><code class="language-jsx">function UsersPage() {
  async function handleClick() {
    const response = await fetch("https://api.example.com/users");
    const data = await response.json();
    // More request and transformation logic here
  }

  return &lt;button onClick={handleClick}&gt;Load&lt;/button&gt;;
}
</code></pre>
<p>This becomes difficult to reuse, test, and maintain when many components repeat the same URL, headers, and error handling.</p>
<h3>14.5 Good: service layer plus a Hook</h3>
<pre><code class="language-jsx">// features/users/hooks/useUsers.js
import { useEffect, useState } from "react";
import { getUsers } from "../services/userService";

export function useUsers() {
  const [users, setUsers] = useState([]);
  const [status, setStatus] = useState("loading");
  const [error, setError] = useState(null);

  useEffect(() =&gt; {
    const controller = new AbortController();

    async function loadUsers() {
      setStatus("loading");
      setError(null);

      try {
        const data = await getUsers({
          signal: controller.signal,
        });

        setUsers(data);
        setStatus("success");
      } catch (err) {
        if (err.name === "AbortError") return;

        setError(err);
        setStatus("error");
      }
    }

    loadUsers();

    return () =&gt; controller.abort();
  }, []);

  return { users, status, error };
}
</code></pre>
<p>Component:</p>
<pre><code class="language-jsx">function UsersPage() {
  const { users, status, error } = useUsers();

  if (status === "loading") return &lt;p&gt;Loading users...&lt;/p&gt;;
  if (status === "error") return &lt;p&gt;{error.message}&lt;/p&gt;;
  if (users.length === 0) return &lt;p&gt;No users found.&lt;/p&gt;;

  return (
    &lt;ul&gt;
      {users.map((user) =&gt; (
        &lt;li key={user.id}&gt;{user.name}&lt;/li&gt;
      ))}
    &lt;/ul&gt;
  );
}
</code></pre>
<p>This example is suitable for a straightforward request. If the application needs caching, request deduplication, background refetching, pagination, or cache invalidation, use a data-fetching library instead of building all of that yourself.</p>
<h3>14.6 When to use TanStack Query or RTK Query</h3>
<p>Consider a server-state library when you need: - Caching and request deduplication. - Background refetching and stale-data handling. - Cache invalidation after mutations. - Pagination or infinite queries. - Consistent loading and error states. - Avoiding repeated manual request logic.</p>
<p>Useful resources: - TanStack Query: <a href="https://tanstack.com/query/latest">https://tanstack.com/query/latest</a> - RTK Query: <a href="https://redux-toolkit.js.org/rtk-query/overview">https://redux-toolkit.js.org/rtk-query/overview</a></p>
<h3>14.7 API call rules</h3>
<ul>
<li>Check <code>response.ok</code> when using <code>fetch</code>; <code>fetch</code> does not reject just because the server returns 404 or 500.</li>
<li>Handle network errors and server errors.</li>
<li>Cancel obsolete requests when appropriate.</li>
<li>Do not expose secret API keys in frontend environment variables.</li>
<li>Do not assume the response always contains valid data.</li>
<li>Avoid triggering the same request from multiple unrelated components when a shared query cache is more suitable.</li>
<li>Keep request and response types/contracts clear.</li>
</ul>
<hr />
<h2>15. Loading, error, empty, and success states</h2>
<p>Every API-driven screen should deliberately handle each state.</p>
<p>State What to show</p>
<hr />
<p>Loading Spinner, skeleton, or progress message Success with data Actual content Success with no data Helpful empty state Error Clear message and retry option where useful</p>
<h3>Bad</h3>
<pre><code class="language-jsx">return &lt;div&gt;{users.map((user) =&gt; user.name)}&lt;/div&gt;;
</code></pre>
<p>This may fail if <code>users</code> is undefined and gives no feedback while the request is running or fails.</p>
<h3>Good</h3>
<pre><code class="language-jsx">if (status === "loading") {
  return &lt;p&gt;Loading...&lt;/p&gt;;
}

if (status === "error") {
  return (
    &lt;div role="alert"&gt;
      &lt;p&gt;Unable to load users.&lt;/p&gt;
      &lt;button onClick={refetch}&gt;Try again&lt;/button&gt;
    &lt;/div&gt;
  );
}

if (users.length === 0) {
  return &lt;p&gt;No users are available yet.&lt;/p&gt;;
}

return (
  &lt;ul&gt;
    {users.map((user) =&gt; (
      &lt;li key={user.id}&gt;{user.name}&lt;/li&gt;
    ))}
  &lt;/ul&gt;
);
</code></pre>
<p>Do not show raw stack traces or internal server details to end users. Log useful diagnostic details through an approved logging system.</p>
<hr />
<h2>16. Forms and validation</h2>
<h3>16.1 Use controlled inputs when React state should own the value</h3>
<pre><code class="language-jsx">function NameForm() {
  const [name, setName] = useState("");

  function handleSubmit(event) {
    event.preventDefault();
    console.log(name);
  }

  return (
    &lt;form onSubmit={handleSubmit}&gt;
      &lt;label htmlFor="name"&gt;Name&lt;/label&gt;
      &lt;input
        id="name"
        value={name}
        onChange={(event) =&gt; setName(event.target.value)}
      /&gt;
      &lt;button type="submit"&gt;Submit&lt;/button&gt;
    &lt;/form&gt;
  );
}
</code></pre>
<h3>16.2 Avoid unnecessary state for every derived form value</h3>
<p>Calculate simple values from the current form state rather than maintaining duplicate values.</p>
<h3>16.3 Use form libraries for complex forms</h3>
<p>Useful options: - React Hook Form: <a href="https://react-hook-form.com/">https://react-hook-form.com/</a> - Zod: <a href="https://zod.dev/">https://zod.dev/</a> - Yup: <a href="https://github.com/jquense/yup">https://github.com/jquense/yup</a></p>
<p>Use a schema validation library when forms have multiple fields, conditional rules, nested data, or shared validation requirements.</p>
<h3>16.4 Validate on both client and server</h3>
<p>Client-side validation improves the user experience. Backend validation is still required because client code can be bypassed.</p>
<h3>16.5 Handle submission state</h3>
<p>Prevent accidental duplicate submissions, show progress, display field-level errors where possible, and provide a clear success result.</p>
<hr />
<h2>17. Rendering and performance</h2>
<h3>17.1 Use stable keys</h3>
<h3>Bad</h3>
<pre><code class="language-jsx">users.map((user, index) =&gt; (
  &lt;UserCard key={index} user={user} /&gt;
))
</code></pre>
<h3>Good</h3>
<pre><code class="language-jsx">users.map((user) =&gt; (
  &lt;UserCard key={user.id} user={user} /&gt;
))
</code></pre>
<p>Use a stable unique ID from the data. Index keys are only reasonable for static lists that never reorder, insert, or delete items.</p>
<h3>17.2 Avoid unnecessary re-renders</h3>
<ul>
<li>Keep state close to the components that use it.</li>
<li>Avoid changing Context values unnecessarily.</li>
<li>Avoid recreating large derived data without need.</li>
<li>Use memoization when profiling shows it helps.</li>
<li>Split large components when it improves ownership and rendering boundaries.</li>
</ul>
<h3>17.3 Lazy-load route-level components</h3>
<pre><code class="language-jsx">import { lazy, Suspense } from "react";

const ReportsPage = lazy(() =&gt; import("./pages/ReportsPage"));

function App() {
  return (
    &lt;Suspense fallback={&lt;p&gt;Loading page...&lt;/p&gt;}&gt;
      &lt;ReportsPage /&gt;
    &lt;/Suspense&gt;
  );
}
</code></pre>
<p>Use a router's route-level lazy-loading support where available.</p>
<h3>17.4 Virtualize large lists</h3>
<p>For thousands of rows, rendering every row can be expensive. Use pagination or a virtualization library such as: - TanStack Virtual: <a href="https://tanstack.com/virtual/latest">https://tanstack.com/virtual/latest</a></p>
<h3>17.5 Optimize assets</h3>
<ul>
<li>Compress images.</li>
<li>Prefer modern formats such as WebP or AVIF where suitable.</li>
<li>Lazy-load offscreen images.</li>
<li>Set image width and height to reduce layout shifts.</li>
<li>Avoid shipping large assets that are not needed on the current page.</li>
</ul>
<h3>17.6 Measure before optimizing</h3>
<p>Use React Developer Tools Profiler and browser performance tools to identify actual bottlenecks. Avoid adding <code>memo</code>, <code>useMemo</code>, and <code>useCallback</code> everywhere.</p>
<hr />
<h2>18. Routing and code splitting</h2>
<p>For multi-page applications, use a routing library such as React Router.</p>
<p>Good routing practices: - Keep route definitions in a clear location. - Use layouts for shared navigation and page structure. - Handle unknown routes with a not-found page. - Protect private routes based on authentication and authorization checks. - Lazy-load large route components where appropriate. - Do not treat hiding a frontend route as backend authorization; protect API endpoints on the server too.</p>
<p>React Router documentation: <a href="https://reactrouter.com/">https://reactrouter.com/</a></p>
<hr />
<h2>19. Error boundaries and error handling</h2>
<h3>19.1 Use error boundaries for render errors</h3>
<p>Error boundaries catch errors thrown while rendering descendant components and show a fallback UI. They do not automatically catch errors in event handlers or asynchronous callbacks.</p>
<p>A class-based boundary remains a common implementation:</p>
<pre><code class="language-jsx">import { Component } from "react";

class ErrorBoundary extends Component {
  state = { hasError: false };

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    // Send diagnostic details to an approved logging service.
    console.error(error, info);
  }

  render() {
    if (this.state.hasError) {
      return &lt;h2&gt;Something went wrong.&lt;/h2&gt;;
    }

    return this.props.children;
  }
}
</code></pre>
<p>Usage:</p>
<pre><code class="language-jsx">&lt;ErrorBoundary&gt;
  &lt;Dashboard /&gt;
&lt;/ErrorBoundary&gt;
</code></pre>
<p>Place boundaries around meaningful parts of the application so one broken area does not necessarily take down the entire UI.</p>
<h3>19.2 Handle event and API errors explicitly</h3>
<pre><code class="language-jsx">async function handleSave() {
  try {
    setStatus("loading");
    await saveUser(formData);
    setStatus("success");
  } catch (error) {
    setStatus("error");
    setErrorMessage("Unable to save your changes. Please try again.");
  }
}
</code></pre>
<p>Do not assume an error boundary will catch this asynchronous failure. Handle it in the request or event flow.</p>
<h3>19.3 Do not swallow errors</h3>
<p>Bad:</p>
<pre><code class="language-jsx">try {
  await saveUser();
} catch (error) {
  // Nothing happens
}
</code></pre>
<p>Good:</p>
<pre><code class="language-jsx">try {
  await saveUser();
} catch (error) {
  setErrorMessage("Save failed. Please try again.");
  reportError(error);
}
</code></pre>
<p>Use an appropriate error-reporting mechanism for production; avoid logging sensitive data.</p>
<hr />
<h2>20. Security and accessibility</h2>
<h3>20.1 Security</h3>
<ul>
<li>Never put private API secrets in frontend code or frontend environment variables.</li>
<li>Validate and authorize sensitive operations on the backend.</li>
<li>Treat API responses and user input as untrusted.</li>
<li>Avoid <code>dangerouslySetInnerHTML</code> unless HTML rendering is genuinely required and the content is sanitized with a trusted sanitizer.</li>
<li>Use secure authentication and session practices appropriate to the application.</li>
<li>Avoid putting sensitive information in URLs, logs, or browser storage.</li>
<li>Keep dependencies updated and review security advisories.</li>
</ul>
<h3>Bad: rendering untrusted HTML</h3>
<pre><code class="language-jsx">&lt;div dangerouslySetInnerHTML={{ __html: userProvidedHtml }} /&gt;
</code></pre>
<h3>Better: render text normally</h3>
<pre><code class="language-jsx">&lt;p&gt;{userProvidedText}&lt;/p&gt;
</code></pre>
<p>If rich HTML is a requirement, sanitize it using a trusted library and a carefully defined policy.</p>
<h3>20.2 Accessibility</h3>
<p>Use semantic HTML:</p>
<pre><code class="language-jsx">&lt;button type="button" onClick={handleSave}&gt;
  Save
&lt;/button&gt;
</code></pre>
<p>Avoid using a clickable <code>div</code> when a button is appropriate.</p>
<p>Also: - Associate labels with form fields. - Provide useful alternative text for meaningful images. - Ensure keyboard navigation works. - Use visible focus styles. - Use <code>aria-live</code> or suitable status semantics for important dynamic feedback. - Give dialogs correct focus management and keyboard behaviour. - Do not rely on colour alone to communicate status.</p>
<hr />
<h2>21. Testing</h2>
<p>Test behaviour that matters to users, not just implementation details.</p>
<h3>21.1 Useful tools</h3>
<ul>
<li>Vitest: <a href="https://vitest.dev/">https://vitest.dev/</a></li>
<li>React Testing Library: <a href="https://testing-library.com/docs/react-testing-library/intro/">https://testing-library.com/docs/react-testing-library/intro/</a></li>
<li>Playwright: <a href="https://playwright.dev/">https://playwright.dev/</a></li>
<li>Cypress: <a href="https://www.cypress.io/">https://www.cypress.io/</a></li>
</ul>
<h3>21.2 Component test example</h3>
<pre><code class="language-jsx">import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Counter } from "./Counter";

test("increments the counter when clicked", async () =&gt; {
  const user = userEvent.setup();

  render(&lt;Counter /&gt;);

  await user.click(
    screen.getByRole("button", { name: /count/i })
  );

  expect(screen.getByText("Count: 1")).toBeInTheDocument();
});
</code></pre>
<h3>21.3 What to test</h3>
<ul>
<li>Important user interactions.</li>
<li>Form validation and submission.</li>
<li>Loading, error, empty, and success states.</li>
<li>Conditional rendering.</li>
<li>Reducer transitions and utility functions.</li>
<li>Critical end-to-end flows such as login, checkout, and record creation.</li>
</ul>
<p>Avoid tests that depend heavily on internal component state or exact implementation details. Prefer queries by role and accessible name.</p>
<hr />
<h2>22. Code quality and maintainability</h2>
<h3>22.1 Use ESLint</h3>
<p>ESLint catches potential bugs and inconsistent patterns. For React projects, configure the React and Hooks rules and follow the project's framework recommendations.</p>
<h3>22.2 Use Prettier</h3>
<p>Prettier formats code consistently. Agree on formatting rules and run formatting automatically where possible.</p>
<h3>22.3 Use clear names</h3>
<p>Bad:</p>
<pre><code class="language-jsx">const d = users.filter((x) =&gt; x.a);
</code></pre>
<p>Good:</p>
<pre><code class="language-jsx">const activeUsers = users.filter((user) =&gt; user.isActive);
</code></pre>
<h3>22.4 Avoid magic values</h3>
<p>Bad:</p>
<pre><code class="language-jsx">if (user.role === 3) {
  // ...
}
</code></pre>
<p>Good:</p>
<pre><code class="language-jsx">const USER_ROLES = {
  ADMIN: "admin",
  MEMBER: "member",
};

if (user.role === USER_ROLES.ADMIN) {
  // ...
}
</code></pre>
<p>Use constants when they make meaning clearer; do not create a constants file for every literal.</p>
<h3>22.5 Avoid duplicate logic</h3>
<p>If multiple features repeat the same meaningful business rule, extract it into a utility, custom Hook, or service at the appropriate level.</p>
<h3>22.6 Keep dependencies intentional</h3>
<p>Before installing a package: - Check whether the platform or React already provides the feature. - Check maintenance activity and compatibility. - Check package size and security. - Avoid adding a library for a trivial one-line task.</p>
<hr />
<h2>23. Example feature structure</h2>
<p>Here is a practical structure for a Users feature in a medium-sized application:</p>
<pre><code class="language-text">src/
├── app/
│   ├── App.jsx
│   ├── routes.jsx
│   └── store.js
│
├── features/
│   └── users/
│       ├── components/
│       │   ├── UserCard.jsx
│       │   ├── UserFilters.jsx
│       │   └── UserForm.jsx
│       │
│       ├── hooks/
│       │   └── useUsers.js
│       │
│       ├── services/
│       │   └── userService.js
│       │
│       ├── utils/
│       │   └── userHelpers.js
│       │
│       ├── UsersPage.jsx
│       └── UserDetailsPage.jsx
│
├── shared/
│   ├── components/
│   │   ├── Button.jsx
│   │   ├── Modal.jsx
│   │   └── Spinner.jsx
│   ├── hooks/
│   └── utils/
│
├── services/
│   └── apiClient.js
│
├── assets/
├── styles/
└── main.jsx
</code></pre>
<h3>Responsibility of each area</h3>
<p>Folder Responsibility</p>
<hr />
<p><code>app/</code> Application setup, routes, providers, store <code>features/</code> Feature-specific pages, components, Hooks, services <code>shared/components/</code> Reusable UI components <code>shared/hooks/</code> Reusable cross-feature Hooks <code>shared/utils/</code> General helper functions <code>services/</code> Shared API client and network configuration <code>assets/</code> Images, icons, fonts <code>styles/</code> Global styles and design tokens</p>
<p>Do not force every feature to have every folder. Add files and folders as the feature needs them.</p>
<hr />
<h2>24. Code review checklist</h2>
<p>Use this checklist before raising a pull request or completing a feature.</p>
<h3>Components and structure</h3>
<ul>
<li><input disabled type="checkbox" /> Components have clear responsibilities.</li>
<li><input disabled type="checkbox" /> Large components are split where it improves clarity.</li>
<li><input disabled type="checkbox" /> Component and file names follow the project convention.</li>
<li><input disabled type="checkbox" /> Feature-specific code is grouped together.</li>
<li><input disabled type="checkbox" /> Reusable components are genuinely reusable.</li>
</ul>
<h3>State and Hooks</h3>
<ul>
<li><input disabled type="checkbox" /> State is kept as local as possible.</li>
<li><input disabled type="checkbox" /> Derived values are calculated instead of duplicated in state.</li>
<li><input disabled type="checkbox" /> State arrays and objects are updated immutably.</li>
<li><input disabled type="checkbox" /> Functional updates are used when the next value depends on the previous value.</li>
<li><input disabled type="checkbox" /> Hooks are called only at the top level.</li>
<li><input disabled type="checkbox" /> <code>useEffect</code> is used for external synchronization, not routine calculations.</li>
<li><input disabled type="checkbox" /> Effects have correct dependencies and cleanup where required.</li>
<li><input disabled type="checkbox" /> Memoization is used only when useful.</li>
</ul>
<h3>Context and state management</h3>
<ul>
<li><input disabled type="checkbox" /> Context is used only for values needed by multiple descendants.</li>
<li><input disabled type="checkbox" /> Context providers are not overloaded with unrelated state.</li>
<li><input disabled type="checkbox" /> <code>useReducer</code> is used when transitions are complex enough to justify it.</li>
<li><input disabled type="checkbox" /> Redux Toolkit is used only when shared client-state needs justify a store.</li>
<li><input disabled type="checkbox" /> Server data is handled with an appropriate fetching and caching strategy.</li>
</ul>
<h3>API and error handling</h3>
<ul>
<li><input disabled type="checkbox" /> API calls are separated into service or query modules.</li>
<li><input disabled type="checkbox" /> HTTP error responses are checked.</li>
<li><input disabled type="checkbox" /> Loading, error, empty, and success states are handled.</li>
<li><input disabled type="checkbox" /> Obsolete requests are cancelled or safely ignored where needed.</li>
<li><input disabled type="checkbox" /> Errors are not silently swallowed.</li>
<li><input disabled type="checkbox" /> No private API secrets are exposed in frontend code.</li>
</ul>
<h3>UI, performance, and quality</h3>
<ul>
<li><input disabled type="checkbox" /> Lists use stable keys.</li>
<li><input disabled type="checkbox" /> Forms have labels and validation.</li>
<li><input disabled type="checkbox" /> Interactive elements are keyboard accessible.</li>
<li><input disabled type="checkbox" /> Large lists use pagination or virtualization when needed.</li>
<li><input disabled type="checkbox" /> Images and route bundles are optimized where appropriate.</li>
<li><input disabled type="checkbox" /> Important behaviour has tests.</li>
<li><input disabled type="checkbox" /> ESLint and formatting checks pass.</li>
<li><input disabled type="checkbox" /> No unnecessary dependencies or dead code are introduced.</li>
</ul>
<hr />
<h2>25. Useful official resources</h2>
<p>Use the official documentation as the source of truth, especially because React APIs and recommended patterns evolve.</p>
<h3>React fundamentals and Hooks</h3>
<ul>
<li>React Learn: <a href="https://react.dev/learn">https://react.dev/learn</a></li>
<li>Describing the UI: <a href="https://react.dev/learn/describing-the-ui">https://react.dev/learn/describing-the-ui</a></li>
<li>Adding Interactivity: <a href="https://react.dev/learn/adding-interactivity">https://react.dev/learn/adding-interactivity</a></li>
<li>Managing State: <a href="https://react.dev/learn/managing-state">https://react.dev/learn/managing-state</a></li>
<li>Escape Hatches and Effects: <a href="https://react.dev/learn/escape-hatches">https://react.dev/learn/escape-hatches</a></li>
<li>Built-in Hooks: <a href="https://react.dev/reference/react/hooks">https://react.dev/reference/react/hooks</a></li>
<li>Rules of Hooks: <a href="https://react.dev/reference/rules/rules-of-hooks">https://react.dev/reference/rules/rules-of-hooks</a></li>
<li>You Might Not Need an Effect: <a href="https://react.dev/learn/you-might-not-need-an-effect">https://react.dev/learn/you-might-not-need-an-effect</a></li>
</ul>
<h3>State management</h3>
<ul>
<li>Context: <a href="https://react.dev/learn/passing-data-deeply-with-context">https://react.dev/learn/passing-data-deeply-with-context</a></li>
<li>Reducer: <a href="https://react.dev/learn/extracting-state-logic-into-a-reducer">https://react.dev/learn/extracting-state-logic-into-a-reducer</a></li>
<li>Scaling with Reducer and Context: <a href="https://react.dev/learn/scaling-up-with-reducer-and-context">https://react.dev/learn/scaling-up-with-reducer-and-context</a></li>
<li>Redux Toolkit: <a href="https://redux-toolkit.js.org/">https://redux-toolkit.js.org/</a></li>
<li>RTK Query: <a href="https://redux-toolkit.js.org/rtk-query/overview">https://redux-toolkit.js.org/rtk-query/overview</a></li>
<li>TanStack Query: <a href="https://tanstack.com/query/latest">https://tanstack.com/query/latest</a></li>
</ul>
<h3>Quality and testing</h3>
<ul>
<li>React Developer Tools: <a href="https://react.dev/learn/react-developer-tools">https://react.dev/learn/react-developer-tools</a></li>
<li>React Testing Library: <a href="https://testing-library.com/docs/react-testing-library/intro/">https://testing-library.com/docs/react-testing-library/intro/</a></li>
<li>Vitest: <a href="https://vitest.dev/">https://vitest.dev/</a></li>
<li>Playwright: <a href="https://playwright.dev/">https://playwright.dev/</a></li>
<li>Web Accessibility: <a href="https://web.dev/learn/accessibility">https://web.dev/learn/accessibility</a></li>
</ul>
<hr />
<h2>Final guidance</h2>
<p>Do not try to introduce every React tool into every project.</p>
<p>A sensible starting point for most applications is:</p>
<ol>
<li>Function components with clear responsibilities.</li>
<li><code>useState</code> for simple local state.</li>
<li><code>useEffect</code> only for synchronization with external systems.</li>
<li>Custom Hooks for reusable behaviour.</li>
<li>Context for a small number of shared values.</li>
<li><code>useReducer</code> for complex local state transitions.</li>
<li>Redux Toolkit when shared client-state complexity justifies it.</li>
<li>TanStack Query or RTK Query for server-state fetching and caching.</li>
<li>A feature-based folder structure as the application grows.</li>
<li>Tests, accessibility, and error handling as part of normal development---not as last-minute additions.</li>
</ol>
]]></content:encoded></item></channel></rss>