react

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React

React

Instructions

说明

This project uses React 19 with TypeScript and the React Compiler enabled via
babel-plugin-react-compiler
.
本项目使用React 19 + TypeScript,并通过
babel-plugin-react-compiler
启用了React Compiler。

Component Structure

组件结构

  • Pages live in
    resources/js/pages/
    and are rendered via Inertia
  • Shared components live in
    resources/js/components/
  • UI primitives (shadcn/ui) live in
    resources/js/components/ui/
  • Custom hooks live in
    resources/js/hooks/
  • 页面文件存放于
    resources/js/pages/
    ,通过Inertia进行渲染
  • 共享组件存放于
    resources/js/components/
  • UI基础组件(shadcn/ui)存放于
    resources/js/components/ui/
  • 自定义Hooks存放于
    resources/js/hooks/

Page Props from Controllers

从控制器获取页面Props

Always receive page data as props from the Laravel controller via Inertia. Do NOT fetch data client-side or use
useEffect
to load page data.
<code-snippet name="Controller Passing Props" lang="php"> // In Laravel controller public function edit(Project $project): Response { return Inertia::render('projects/edit', [ 'project' => $project->load('client'), 'clients' => Client::orderBy('name')->get(), ]); } </code-snippet> <code-snippet name="Page Receiving Props" lang="tsx"> // In React page component import { Project, Client } from '@/types';
interface EditProjectProps { project: Project; clients: Client[]; }
export default function EditProject({ project, clients }: EditProjectProps) { return ( <Form action={update({ project: project.id }).url} method="patch"> {/* Form fields using project and clients data */} </Form> ); } </code-snippet>
This pattern ensures:
  • State consistency - The server is the single source of truth; no client-side caching or stale data issues
  • Data is loaded server-side before the page renders
  • No loading states needed for initial page data
  • SEO-friendly server-rendered content
  • Type-safe props matching controller output
始终通过Inertia从Laravel控制器接收页面数据作为Props。不要在客户端获取数据,也不要使用
useEffect
加载页面数据。
<code-snippet name="Controller Passing Props" lang="php"> // In Laravel controller public function edit(Project $project): Response { return Inertia::render('projects/edit', [ 'project' => $project->load('client'), 'clients' => Client::orderBy('name')->get(), ]); } </code-snippet> <code-snippet name="Page Receiving Props" lang="tsx"> // In React page component import { Project, Client } from '@/types';
interface EditProjectProps { project: Project; clients: Client[]; }
export default function EditProject({ project, clients }: EditProjectProps) { return ( <Form action={update({ project: project.id }).url} method="patch"> {/* Form fields using project and clients data */} </Form> ); } </code-snippet>
该模式可确保:
  • 状态一致性 - 服务器作为单一数据源,不存在客户端缓存或数据过期问题
  • 数据在页面渲染前已在服务器端加载完成
  • 初始页面数据无需加载状态
  • 支持SEO的服务器渲染内容
  • 与控制器输出匹配的类型安全Props

TypeScript Conventions

TypeScript规范

  • All component props should be typed explicitly
  • Use interfaces for object shapes, defined in
    resources/js/types/index.d.ts
  • Prefer
    React.ComponentProps<"element">
    for extending native element props
  • Use path aliases:
    @/components
    ,
    @/hooks
    ,
    @/lib
    ,
    @/types
<code-snippet name="Typed Component Example" lang="tsx"> import { cn } from '@/lib/utils';
interface CardProps { title: string; description?: string; className?: string; children: React.ReactNode; }
export function Card({ title, description, className, children }: CardProps) { return ( <div className={cn('rounded-lg border p-4', className)}> <h3 className="font-semibold">{title}</h3> {description && <p className="text-muted-foreground">{description}</p>} {children} </div> ); } </code-snippet>
  • 所有组件Props都应显式定义类型
  • 对象结构使用接口定义,存放于
    resources/js/types/index.d.ts
  • 优先使用
    React.ComponentProps<"element">
    扩展原生元素Props
  • 使用路径别名:
    @/components
    ,
    @/hooks
    ,
    @/lib
    ,
    @/types
<code-snippet name="Typed Component Example" lang="tsx"> import { cn } from '@/lib/utils';
interface CardProps { title: string; description?: string; className?: string; children: React.ReactNode; }
export function Card({ title, description, className, children }: CardProps) { return ( <div className={cn('rounded-lg border p-4', className)}> <h3 className="font-semibold">{title}</h3> {description && <p className="text-muted-foreground">{description}</p>} {children} </div> ); } </code-snippet>

React 19 + React Compiler

React 19 + React Compiler

  • The React Compiler automatically memoizes components and hooks
  • Do NOT manually add
    useMemo
    ,
    useCallback
    , or
    React.memo
    unless profiling shows a specific need
  • Write straightforward code and let the compiler optimize
  • React Compiler会自动对组件和Hooks进行记忆化处理
  • 除非性能分析显示有特定需求,否则不要手动添加
    useMemo
    useCallback
    React.memo
  • 编写简洁直观的代码,交由编译器进行优化

State Management

状态管理

  • Use React's built-in
    useState
    and
    useReducer
    for local state
  • Use Inertia's shared data for server-provided state (accessed via
    usePage()
    )
  • For form state, use the Inertia
    <Form>
    component (see inertia skill)
<code-snippet name="Accessing Shared Data" lang="tsx"> import { usePage } from '@inertiajs/react'; import { SharedData } from '@/types';
export function UserGreeting() { const { auth } = usePage<SharedData>().props;
return <span>Hello, {auth.user.name}</span>;
} </code-snippet>
  • 本地状态使用React内置的
    useState
    useReducer
  • 服务器提供的状态使用Inertia的共享数据(通过
    usePage()
    访问)
  • 表单状态使用Inertia的
    <Form>
    组件(参考Inertia相关技能)
<code-snippet name="Accessing Shared Data" lang="tsx"> import { usePage } from '@inertiajs/react'; import { SharedData } from '@/types';
export function UserGreeting() { const { auth } = usePage<SharedData>().props;
return <span>Hello, {auth.user.name}</span>;
} </code-snippet>

Event Handlers

事件处理

  • Use inline arrow functions for simple handlers
  • Extract to named functions only when logic is complex or reused
  • 简单处理函数使用内联箭头函数
  • 仅当逻辑复杂或需要复用时,才提取为命名函数

Conditional Rendering

条件渲染

  • Use early returns for guard clauses
  • Prefer
    &&
    for simple conditionals, ternaries for if/else
  • Use separate components for complex conditional logic
<code-snippet name="Conditional Rendering Patterns" lang="tsx"> // Early return for loading state function DataDisplay({ data, isLoading }: Props) { if (isLoading) { return <Skeleton />; }
return (
    <div>
        {data.items.length > 0 && <ItemList items={data.items} />}
        {data.error ? <ErrorMessage error={data.error} /> : <SuccessIndicator />}
    </div>
);
} </code-snippet>
  • 使用提前返回实现守卫子句
  • 简单条件优先使用
    &&
    ,if/else场景使用三元表达式
  • 复杂条件逻辑使用独立组件实现
<code-snippet name="Conditional Rendering Patterns" lang="tsx"> // Early return for loading state function DataDisplay({ data, isLoading }: Props) { if (isLoading) { return <Skeleton />; }
return (
    <div>
        {data.items.length > 0 && <ItemList items={data.items} />}
        {data.error ? <ErrorMessage error={data.error} /> : <SuccessIndicator />}
    </div>
);
} </code-snippet>

File Naming

文件命名规范

  • Components: PascalCase (
    UserCard.tsx
    )
  • Hooks: camelCase with
    use
    prefix (
    useClipboard.ts
    )
  • Utilities: camelCase (
    formatDate.ts
    )
  • Pages: kebab-case matching the route (
    edit.tsx
    ,
    create.tsx
    ,
    index.tsx
    )
  • 组件:大驼峰命名(
    UserCard.tsx
  • Hooks:小驼峰命名并以
    use
    为前缀(
    useClipboard.ts
  • 工具函数:小驼峰命名(
    formatDate.ts
  • 页面:短横线命名,与路由匹配(
    edit.tsx
    ,
    create.tsx
    ,
    index.tsx