Docs
Best Practices

Best Practices

Comprehensive guide to best practices for React, TypeScript, Prisma, and more in the YouTube Analyzer project

Best Practices

This document consolidates all the best practices for the YouTube Analyzer project into a single, comprehensive guide organized by topic.

Table of Contents

  1. React & JSX
  2. TypeScript
  3. Prisma & Database
  4. API Development
  5. Next.js Specific
  6. Tools & Setup

React & JSX

Component Organization

File Structure

  • One component per file (except for very small related components)
  • Use PascalCase for component files and function names
  • Group related components in feature folders

Component Patterns

  • Use the "use client" directive:
    • Add "use client"; at the top of any component file that uses React hooks
    • This is required for Next.js 13+ Client Components
// Good:
"use client";
 
import { useState } from 'react';
 
export function MyComponent() {
  const [state, setState] = useState(false);
  // ...
}
  • Prefer functional components over class components:
// Good:
function MyComponent({ prop1, prop2 }) {
  return <div>{prop1}</div>;
}
 
// Avoid:
class MyComponent extends React.Component {
  render() {
    return <div>{this.props.prop1}</div>;
  }
}
  • Use named exports for components:
// Good:
export function MyComponent() {...}
 
// Avoid:
export default function() {...}
  • Extract reusable logic into custom hooks
  • Provide default values for optional props

State Management

  • Use React hooks appropriately:

    • useState for component-local state
    • useReducer for complex state logic
    • useContext for global state accessed by many components
    • useMemo and useCallback for performance optimization (only when needed)
  • Keep state as local as possible:

    • Don't lift state higher than necessary
    • Use composition to pass state down when needed

Performance Optimization

  • Avoid unnecessary renders:

    • Use memoization (React.memo, useMemo, useCallback) for expensive computations
    • Avoid creating new objects or functions in render
  • Use proper keys for lists:

    // Good:
    {items.map(item => <ListItem key={item.id} {...item} />)}
     
    // Avoid:
    {items.map((item, index) => <ListItem key={index} {...item} />)}  // Only use index as last resort

JSX Syntax

JSX Quotes and Entities

IMPORTANT: Unescaped entities cause deployment errors even though they may work in local development. Always escape the following characters:

  1. Use proper JSX entity references for special characters:

    • For apostrophes: Use &apos; or &#39; (preferred in JSX) instead of '
      • Example: It&apos;s or It&#39;s instead of It's
    • For quotes: Use &quot; or &#34; instead of "
      • Example: The &quot;example&quot; instead of The "example"
    • For ampersands: Use &amp; instead of &
      • Example: Standard &amp; Poor instead of Standard & Poor
  2. Examples:

    // Good:
    <p>Don&apos;t forget to check the &quot;terms&quot; section</p>
     
    // Avoid:
    <p>Don't forget to check the "terms" section</p>
  3. Methods to handle quotes in JSX:

    • Use single quotes for JSX attributes, double quotes for content:

      <Button className='text-sm'>Click "here"</Button>
    • Escape double quotes in JSX attributes:

      <Button className="text-sm font-\"bold\"">Click</Button>
    • Use template literals for complex string combinations:

      <Button className={`text-sm ${size === "large" ? "p-4" : "p-2"}`}>Click</Button>
    • Use entity references for quotes in content:

      <p>The user said &quot;Hello World&quot;</p>
    • Use curly braces for dynamic content with quotes:

      <p>The user said {"\"Hello World\""}</p>
  4. Examples of problematic code:

    // BAD - Will cause build errors
    <p className="text-"bold"">Click here</p>
    <div style="width: "100%"">Content</div>

Code Style

  • Use consistent indentation (2 spaces)
  • Use semicolons at the end of statements
  • Keep components focused on a single responsibility
  • Use descriptive variable and function names

Imports

  • Group imports by:
    1. React and Next.js imports
    2. Third-party libraries
    3. Project components and utilities
    4. Styles

Props

  • Destructure props in function parameters
  • Use type annotations for all props
  • Document complex props with comments

Testing

  • Write unit tests for components, focusing on user interactions and output
  • Use React Testing Library over Enzyme (focusing on user behavior rather than implementation details)
  • Test happy paths and error states
  • Use mock data for API calls and complex state

TypeScript

Types and Interfaces

  • Use TypeScript interfaces for defining component props:

    interface ButtonProps {
      variant?: 'primary' | 'secondary';
      size?: 'sm' | 'md' | 'lg';
      children: React.ReactNode;
      onClick?: () => void;
    }
  • Export shared types from central files

  • Use type unions for values with a fixed set of options:

    type Status = 'idle' | 'loading' | 'success' | 'error';

Type Safety

  • Avoid using any type - use unknown or proper types instead
  • Use optional chaining (?.) and nullish coalescing (??) for potential undefined/null values
  • Type-check user inputs and API responses
  • Leverage TypeScript's utility types (Partial, Omit, Pick, etc.) when appropriate

Prisma & Database

Working with Prisma Enums

Problem: Type Error with Enums

One of the most common deployment errors occurs when string values are assigned to fields that Prisma expects to be enum values:

// ❌ WRONG: Using string where Prisma enum is expected
interface AutoAnalysisCreateRequest {
  triggerType: string; // Error: Type 'string' is not assignable to type 'TriggerType'
}

Solution: Define Local Type Unions

When working with Prisma enums in API routes or other files:

  1. Define a local type that matches the Prisma enum values:
// ✅ CORRECT: Define a type that mirrors the Prisma enum
type TriggerType = 'manual' | 'auto' | 'per_video' | 'batch_3' | 'batch_5';
 
interface AutoAnalysisCreateRequest {
  triggerType: TriggerType; // Properly typed
}
  1. When validating input, cast to string for operation:
if (!["per_video", "batch_3", "batch_5"].includes(triggerType as string)) {
  return NextResponse.json({ error: "Invalid triggerType" }, { status: 400 });
}

Handling Prisma Enums

There are multiple ways to handle Prisma enums in TypeScript:

  1. Type Union (Recommended for API Routes):

    type TriggerType = 'manual' | 'auto' | 'per_video' | 'batch_3' | 'batch_5';
  2. Direct Import from Generated Client (Not Always Available):

    // This pattern may not work in all environments
    import { Prisma } from '@prisma/client';
    type MyEnum = Prisma.EnumName;
  3. Using as const Arrays:

    const TRIGGER_TYPES = ['manual', 'auto', 'per_video', 'batch_3', 'batch_5'] as const;
    type TriggerType = typeof TRIGGER_TYPES[number];

Query Optimization

  • Use include/select wisely to avoid over-fetching

    // Good - only fetch what you need
    const user = await prisma.user.findUnique({
      where: { id },
      select: {
        id: true,
        name: true,
        email: true,
      }
    });
     
    // Avoid - fetching everything when only a few fields are needed
    const user = await prisma.user.findUnique({ where: { id } });
  • Use transactions for operations that need to be atomic

  • Consider pagination for large data sets

  • Create indexes for frequently queried fields


API Development

Request Validation

  1. Define proper types for request bodies:

    interface MyRequestBody {
      enumField: MyEnumType;
      // other fields
    }
  2. Validate inputs against enum values before using them with Prisma:

    const validEnumValues = ['value1', 'value2', 'value3'];
    if (!validEnumValues.includes(input as string)) {
      return NextResponse.json({ error: "Invalid value" }, { status: 400 });
    }
  3. Use zod or similar libraries for complex validation

Error Handling

  • Use appropriate HTTP status codes for different types of errors
  • Return consistent error response format across all API endpoints
  • Log errors with sufficient context for debugging
  • Handle edge cases explicitly rather than relying on default behaviors

Next.js Specific

Client vs. Server Components

  • Always add "use client"; to files that use React hooks or other client-side features
  • Don't add "use client"; to files that don't need client-side interactivity
  • Remember that all children of a client component are also client components
  • Keep data fetching in server components when possible for better performance

Route Handlers

  • Use proper HTTP methods (GET, POST, PUT, DELETE) based on the operation
  • Return NextResponse objects with appropriate status codes
  • Add proper authentication checks at the beginning of the handler
  • Handle content negotiation when needed (Accept header)

Tools & Setup

VS Code Extensions

  • ESLint: For linting JavaScript and TypeScript
  • Prisma: For schema highlighting and validation
  • Error Lens: To see errors in-line in the editor
  • TypeScript Error Translator: For more readable TypeScript errors

Pre-Deployment Checklist

Before deploying:

  • Run npm run type-check or equivalent
  • Verify any Prisma enum usage has proper typing
  • Test API routes with valid and invalid values
  • Review any type assertions (as keyword usage) for correctness
  • Check for unescaped entities in JSX
  • Ensure all environment variables are set correctly