Skip to content

Latest commit

 

History

History
143 lines (95 loc) · 4.2 KB

react.md

File metadata and controls

143 lines (95 loc) · 4.2 KB

React — Framework Guidance

Scope: organization
Status: enabled
Path: frameworks/react.md


Overview

This document covers how the organization uses React for frontend applications. It covers component patterns, state management, security, and performance conventions.


Component Conventions

Naming and File Structure

  • One component per file. File name matches component name exactly (UserCard.tsx, not user-card.tsx).
  • Co-locate tests with components: UserCard.tsx / UserCard.test.tsx.
  • Shared components live in src/components/. Page-specific components live next to their page file.

Functional Components Only

Do not write new class components. Use function components with hooks.

// ✅ Standard pattern
interface Props {
  userId: number;
  onUpdate: (user: User) => void;
}

export function UserCard({ userId, onUpdate }: Props) {
  // ...
}

Prop Types

All components must have explicit TypeScript prop types. Do not use any in component props. Do not use React.FC<Props> — it adds implicit children and obscures the component signature.


State Management

Local vs. Global

  • Prefer local state (useState, useReducer) unless the state is genuinely shared across distant parts of the tree.
  • Use React Context for low-frequency global state (auth user, theme, locale).
  • Use a server-state library (React Query / TanStack Query) for data fetched from an API — do not store server data in Redux or Zustand.

Data Fetching

Use TanStack Query as the standard for all server data:

const { data: user, isLoading, error } = useQuery({
  queryKey: ['user', userId],
  queryFn: () => fetchUser(userId),
  staleTime: 30_000,
});
  • Define query keys in a central queryKeys.ts file to avoid collision.
  • Set staleTime explicitly — the default of 0 causes unnecessary refetches.
  • Handle error states visibly; do not silently swallow fetch errors.

Security

No dangerouslySetInnerHTML

Never use dangerouslySetInnerHTML without sanitizing the content first with DOMPurify. Treat any instance as a code-review blocking issue.

// ❌ PROHIBITED without sanitization
<div dangerouslySetInnerHTML={{ __html: userContent }} />

// ✅ If HTML rendering is genuinely required
import DOMPurify from 'dompurify';
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userContent) }} />

Secrets and Environment Variables

  • REACT_APP_ / VITE_ prefixed variables are bundled into the client — they are public. Never put API keys, signing secrets, or credentials in them.
  • Backend API calls must go through the organization's API gateway, not directly to third-party services with credentials embedded in client code.

href and src Validation

When building links or image sources from user data, validate the URL scheme:

function SafeLink({ href, children }: { href: string; children: React.ReactNode }) {
  const safe = /^https?:\/\//.test(href) ? href : '#';
  return <a href={safe} rel="noopener noreferrer">{children}</a>;
}

Always add rel="noopener noreferrer" to external links.


Performance

Code Splitting

Lazy-load routes using React.lazy + Suspense:

const SettingsPage = React.lazy(() => import('./pages/SettingsPage'));

<Suspense fallback={<PageSpinner />}>
  <SettingsPage />
</Suspense>

Memoization

  • useMemo and useCallback should be used where there is a measured performance problem, not preemptively everywhere.
  • React.memo is appropriate for components that render frequently with the same props (e.g., list items).

Avoid Inline Object/Function Creation in JSX

// ❌ Creates a new object on every render, breaking memo
<Component style={{ margin: 8 }} />

// ✅ Define outside the render, or use a CSS class
const STYLE = { margin: 8 } as const;
<Component style={STYLE} />

Accessibility

  • Every interactive element must be keyboard-operable.
  • Images require meaningful alt text (empty string for decorative images).
  • Use semantic HTML elements (<button>, <nav>, <main>) before reaching for <div role="...">.
  • Run axe-core in development via the @axe-core/react package — zero violations is the merge bar.