react-structure

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React Structure

React组件结构

When working with React code in this project, always follow these conventions. The goal is to keep UI rendering cleanly separated from logic, making components easier to test, review, and maintain.
在本项目中处理React代码时,请始终遵循以下约定。目标是将UI渲染与逻辑清晰分离,让组件更易于测试、评审和维护。

Component file structure

组件文件结构

Every component lives in its own folder with this layout:
ComponentName/
├── ComponentName.tsx      # UI only — JSX rendering, no business logic
├── useComponentName.ts    # Hook — state, handlers, side effects, data fetching
├── type.ts                # TypeScript types and interfaces for this component
└── componentSlice.ts      # Redux slice (only if this component owns Redux state)
每个组件都存放在独立的文件夹中,结构如下:
ComponentName/
├── ComponentName.tsx      # 仅负责UI — JSX渲染,无业务逻辑
├── useComponentName.ts    # Hook文件 — 状态处理、事件处理器、副作用、数据请求
├── type.ts                # 该组件对应的TypeScript类型与接口
└── componentSlice.ts      # Redux切片(仅当组件拥有独立Redux状态时创建)

What goes where

文件职责划分

ComponentName.tsx (UI file)
  • JSX rendering and layout
  • MUI component composition and styling (
    sx
    props,
    styled()
    )
  • Calls the custom hook to get all data and handlers
  • No
    useState
    ,
    useEffect
    ,
    useSelector
    ,
    useDispatch
    , or business logic here
tsx
// UserList.tsx
import { useUserList } from './useUserList';
import { DataGridPremium } from '@mui/x-data-grid-premium';

export const UserList = () => {
  const { rows, columns, loading, handleRowClick } = useUserList();

  return (
    <DataGridPremium
      rows={rows}
      columns={columns}
      loading={loading}
      onRowClick={handleRowClick}
    />
  );
};
useComponentName.ts (Hook file)
  • All React hooks (
    useState
    ,
    useEffect
    ,
    useMemo
    ,
    useCallback
    )
  • Redux interactions (
    useSelector
    ,
    useDispatch
    )
  • Event handlers and business logic
  • Data transformation and preparation
  • Returns everything the UI file needs as a clean interface
tsx
// useUserList.ts
import { useSelector, useDispatch } from 'react-redux';
import { GridColDef, GridRowParams } from '@mui/x-data-grid-premium';
import { selectUsers, fetchUsers } from './userSlice';
import { User } from './type';

export const useUserList = () => {
  const dispatch = useDispatch();
  const users = useSelector(selectUsers);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    dispatch(fetchUsers()).finally(() => setLoading(false));
  }, [dispatch]);

  const columns: GridColDef<User>[] = useMemo(() => [
    { field: 'name', headerName: 'Name', flex: 1 },
    { field: 'email', headerName: 'Email', flex: 1 },
  ], []);

  const handleRowClick = useCallback((params: GridRowParams<User>) => {
    // handle row click
  }, []);

  return { rows: users, columns, loading, handleRowClick };
};
type.ts
  • Interfaces and types scoped to this component
  • Props types, data models, enum types
tsx
// type.ts
export interface User {
  id: string;
  name: string;
  email: string;
}

export interface UserListProps {
  filterByRole?: string;
}
componentSlice.ts (only when needed)
  • Redux Toolkit slice with state, reducers, async thunks, and selectors
  • Only create this file if the component owns its own piece of Redux state
ComponentName.tsx(UI文件)
  • JSX渲染与布局
  • MUI组件组合与样式(
    sx
    属性、
    styled()
    方法)
  • 调用自定义hook获取所需的所有数据和处理器
  • 此处禁止使用
    useState
    、
    useEffect
    、
    useSelector
    、
    useDispatch
    或任何业务逻辑
tsx
// UserList.tsx
import { useUserList } from './useUserList';
import { DataGridPremium } from '@mui/x-data-grid-premium';

export const UserList = () => {
  const { rows, columns, loading, handleRowClick } = useUserList();

  return (
    <DataGridPremium
      rows={rows}
      columns={columns}
      loading={loading}
      onRowClick={handleRowClick}
    />
  );
};
useComponentName.ts(Hook文件)
  • 所有React hooks(
    useState
    、
    useEffect
    、
    useMemo
    、
    useCallback
    )
  • Redux交互(
    useSelector
    、
    useDispatch
    )
  • 事件处理器与业务逻辑
  • 数据转换与预处理
  • 将UI文件所需的所有内容以清晰的接口形式返回
tsx
// useUserList.ts
import { useSelector, useDispatch } from 'react-redux';
import { GridColDef, GridRowParams } from '@mui/x-data-grid-premium';
import { selectUsers, fetchUsers } from './userSlice';
import { User } from './type';

export const useUserList = () => {
  const dispatch = useDispatch();
  const users = useSelector(selectUsers);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    dispatch(fetchUsers()).finally(() => setLoading(false));
  }, [dispatch]);

  const columns: GridColDef<User>[] = useMemo(() => [
    { field: 'name', headerName: 'Name', flex: 1 },
    { field: 'email', headerName: 'Email', flex: 1 },
  ], []);

  const handleRowClick = useCallback((params: GridRowParams<User>) => {
    // handle row click
  }, []);

  return { rows: users, columns, loading, handleRowClick };
};
type.ts
  • 该组件专属的接口与类型
  • 组件属性类型、数据模型、枚举类型
tsx
// type.ts
export interface User {
  id: string;
  name: string;
  email: string;
}

export interface UserListProps {
  filterByRole?: string;
}
componentSlice.ts(仅在需要时创建)
  • 包含状态、reducers、异步thunks和选择器的Redux Toolkit切片
  • 仅当组件拥有独立的Redux状态时才创建此文件

Tech stack

技术栈

  • Language: TypeScript (strict — always type props, state, and return values)
  • State management: Redux Toolkit (
    createSlice
    ,
    createAsyncThunk
    )
  • Styling: MUI (Material UI) — use
    sx
    prop or
    styled()
    for custom styling
  • Tables: Always use
    @mui/x-data-grid-premium
    (
    DataGridPremium
    ) for any table or data grid. Never use plain HTML tables or basic MUI
    Table
    components when displaying tabular data.
  • 语言: TypeScript(严格模式 — 始终为属性、状态和返回值添加类型)
  • 状态管理: Redux Toolkit(
    createSlice
    、
    createAsyncThunk
    )
  • 样式: MUI (Material UI) — 使用
    sx
    属性或
    styled()
    方法实现自定义样式
  • 表格: 所有表格或数据网格均需使用
    @mui/x-data-grid-premium
    (
    DataGridPremium
    )。展示表格数据时,禁止使用纯HTML表格或基础MUI
    Table
    组件。

Planning — always show a flowchart

规划阶段 — 务必展示流程图

Before writing or modifying any code, present a Mermaid flowchart that shows the function/data flow of what you're about to change or create. This helps the user visualize the impact before any code is written.
The flowchart should cover:
  • Which files are involved (hook, UI, slice, types)
  • How data flows between them (props, dispatch, selectors, hook return values)
  • The user actions and event handler chain
Example — adding a delete feature to UserList:
mermaid
flowchart TD
    A[User clicks delete button] --> B[UserList.tsx calls handleDelete]
    B --> C[useUserList.ts handleDelete]
    C --> D[dispatch deleteUser thunk]
    D --> E[userSlice.ts async thunk → API call]
    E --> F[userSlice reducer updates state]
    F --> G[useSelector picks up new state]
    G --> H[UserList.tsx re-renders without deleted row]
Always show this flowchart and get confirmation before proceeding with implementation.
在编写或修改任何代码之前,请先提供Mermaid流程图,展示即将修改或创建的功能/数据流。这有助于用户在代码编写前直观了解改动影响。
流程图应涵盖:
  • 涉及的文件(hook、UI、切片、类型)
  • 文件间的数据流向(属性、dispatch、选择器、hook返回值)
  • 用户操作与事件处理器链
示例 — 为UserList添加删除功能:
mermaid
flowchart TD
    A[用户点击删除按钮] --> B[UserList.tsx调用handleDelete]
    B --> C[useUserList.ts中的handleDelete]
    C --> D[dispatch deleteUser thunk]
    D --> E[userSlice.ts中的异步thunk → API调用]
    E --> F[userSlice reducer更新状态]
    F --> G[useSelector获取新状态]
    G --> H[UserList.tsx重新渲染,移除已删除行]
在开始实现前,务必展示此流程图并获得用户确认。

When modifying existing components

修改现有组件时的规范

If you're editing a component that doesn't follow this structure yet, refactor it to match:
  1. Extract all logic into
    useComponentName.ts
  2. Extract types into
    type.ts
  3. Leave only JSX rendering in
    ComponentName.tsx
  4. If there's inline Redux state, move it to
    componentSlice.ts
If the change is very small (e.g., fixing a typo in a label), use judgment — don't force a full refactor for a one-word change. But for any meaningful logic or UI change, apply the structure.
如果编辑的组件尚未遵循此结构,请将其重构为符合规范的形式:
  1. 将所有逻辑提取到
    useComponentName.ts
    中
  2. 将类型提取到
    type.ts
    中
  3. 仅在
    ComponentName.tsx
    中保留JSX渲染代码
  4. 如果存在内联Redux状态,将其迁移到
    componentSlice.ts
如果改动非常小(例如修复标签中的拼写错误),可灵活判断 — 无需为单个单词的改动进行完整重构。但对于任何有意义的逻辑或UI修改,均需遵循此结构。