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
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
- Add
// 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:
useStatefor component-local stateuseReducerfor complex state logicuseContextfor global state accessed by many componentsuseMemoanduseCallbackfor 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 memoization (
-
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:
-
Use proper JSX entity references for special characters:
- For apostrophes: Use
'or'(preferred in JSX) instead of'- Example:
It'sorIt'sinstead ofIt's
- Example:
- For quotes: Use
"or"instead of"- Example:
The "example"instead ofThe "example"
- Example:
- For ampersands: Use
&instead of&- Example:
Standard & Poorinstead ofStandard & Poor
- Example:
- For apostrophes: Use
-
Examples:
// Good: <p>Don't forget to check the "terms" section</p> // Avoid: <p>Don't forget to check the "terms" section</p> -
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 "Hello World"</p> -
Use curly braces for dynamic content with quotes:
<p>The user said {"\"Hello World\""}</p>
-
-
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:
- React and Next.js imports
- Third-party libraries
- Project components and utilities
- 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
anytype - useunknownor 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:
- 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
}- 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:
-
Type Union (Recommended for API Routes):
type TriggerType = 'manual' | 'auto' | 'per_video' | 'batch_3' | 'batch_5'; -
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; -
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
-
Define proper types for request bodies:
interface MyRequestBody { enumField: MyEnumType; // other fields } -
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 }); } -
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 (
Acceptheader)
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-checkor equivalent - Verify any Prisma enum usage has proper typing
- Test API routes with valid and invalid values
- Review any type assertions (
askeyword usage) for correctness - Check for unescaped entities in JSX
- Ensure all environment variables are set correctly