Loading...
Loading...
Compare original and translation side by side
references/best-practices.mdreferences/useeffect-patterns.mdtanstacktanstackreferences/best-practices.mdreferences/useeffect-patterns.mdtanstacktanstackuse()useOptimistic()use()useOptimistic()| Priority | Tool | Use Case |
|---|---|---|
| 1 | | Component-specific UI state |
| 2 | Zustand | Shared client state across components |
| 3 | TanStack Query | Server state and data synchronization |
| 4 | URL state | Shareable application state (TanStack Router) |
| 优先级 | 工具 | 使用场景 |
|---|---|---|
| 1 | | 组件专属UI状态 |
| 2 | Zustand | 跨组件共享客户端状态 |
| 3 | TanStack Query | 服务端状态与数据同步 |
| 4 | URL状态 | 可共享的应用状态(TanStack Router) |
| Situation | DON'T | DO |
|---|---|---|
| Derived state from props/state | | Calculate during render |
| Expensive calculations | | |
| Reset state on prop change | | |
| User event responses | | Event handler directly |
| Notify parent of changes | | Call in event handler |
| Fetch data | | |
| 场景 | 不要做 | 应该做 |
|---|---|---|
| 从props/state派生状态 | | 在渲染阶段直接计算 |
| 昂贵的计算 | 使用 | 使用 |
| 当props变化时重置状态 | | 使用 |
| 响应用户事件 | 监听state的 | 直接使用事件处理程序 |
| 通知父组件变化 | | 在事件处理程序中调用 |
| 获取数据 | 无清理逻辑的 | 带清理逻辑的 |
useSyncExternalStoreuseSyncExternalStoreconst fullName = firstName + ' ' + lastNameconst fullName = firstName + ' ' + lastName// CORRECT: Type props directly (never use React.FC)
interface ButtonProps {
variant: "primary" | "secondary";
children: React.ReactNode;
}
function Button({ variant, children }: ButtonProps) {
return <button className={variant}>{children}</button>;
}
// Extending HTML elements
interface InputProps extends React.ComponentProps<"input"> {
label: string;
}// 正确做法:直接为props定义类型(切勿使用React.FC)
interface ButtonProps {
variant: "primary" | "secondary";
children: React.ReactNode;
}
function Button({ variant, children }: ButtonProps) {
return <button className={variant}>{children}</button>;
}
// 扩展HTML元素
interface InputProps extends React.ComponentProps<"input"> {
label: string;
}useXxx// State-like hook returns array
function useToggle(initial = false) {
const [value, setValue] = useState(initial);
const toggle = useCallback(() => setValue((v) => !v), []);
return [value, toggle] as const;
}
// Complex hook returns object
function useUser(id: string) {
const query = useQuery({ queryKey: ["user", id], queryFn: () => fetchUser(id) });
return {
user: query.data,
isLoading: query.isLoading,
error: query.error,
refetch: query.refetch,
};
}useXxx// 类状态的Hook返回数组
function useToggle(initial = false) {
const [value, setValue] = useState(initial);
const toggle = useCallback(() => setValue((v) => !v), []);
return [value, toggle] as const;
}
// 复杂Hook返回对象
function useUser(id: string) {
const query = useQuery({ queryKey: ["user", id], queryFn: () => fetchUser(id) });
return {
user: query.data,
isLoading: query.isLoading,
error: query.error,
refetch: query.refetch,
};
}// CORRECT: Hook handles all logic, component handles rendering
function useIssueSearch(projectId: string) {
const [query, setQuery] = useState("");
const [filters, setFilters] = useState<Filters>({});
const issues = useQuery({
queryKey: ["issues", projectId, query, filters],
queryFn: () => searchIssues(projectId, query, filters),
});
return {
query,
setQuery,
filters,
setFilters,
issues: issues.data ?? [],
isLoading: issues.isLoading,
};
}
function IssueList({ projectId }: { projectId: string }) {
const { query, setQuery, issues, isLoading } = useIssueSearch(projectId);
return (
<div>
<SearchInput value={query} onChange={setQuery} />
{isLoading ? <Loading /> : <IssueTable issues={issues} />}
</div>
);
}// 正确做法:Hook处理所有逻辑,组件负责渲染
function useIssueSearch(projectId: string) {
const [query, setQuery] = useState("");
const [filters, setFilters] = useState<Filters>({});
const issues = useQuery({
queryKey: ["issues", projectId, query, filters],
queryFn: () => searchIssues(projectId, query, filters),
});
return {
query,
setQuery,
filters,
setFilters,
issues: issues.data ?? [],
isLoading: issues.isLoading,
};
}
function IssueList({ projectId }: { projectId: string }) {
const { query, setQuery, issues, isLoading } = useIssueSearch(projectId);
return (
<div>
<SearchInput value={query} onChange={setQuery} />
{isLoading ? <Loading /> : <IssueTable issues={issues} />}
</div>
);
}| Type | Pattern | Example |
|---|---|---|
| Components | kebab-case.tsx | |
| Hooks | use-kebab-case.ts | |
| Utilities | camelCase.ts | |
| Types | types.ts | |
| Tests | *.test.tsx | |
| 类型 | 模式 | 示例 |
|---|---|---|
| 组件 | kebab-case.tsx | |
| Hooks | use-kebab-case.ts | |
| 工具函数 | camelCase.ts | |
| 类型定义 | types.ts | |
| 测试文件 | *.test.tsx | |
import { describe, it, expect, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import { renderHook, act } from "@testing-library/react";
describe("MyComponent", () => {
it("renders correctly", () => {
render(<MyComponent />);
expect(screen.getByText("Hello")).toBeInTheDocument();
});
});
describe("useMyHook", () => {
it("returns expected value", () => {
const { result } = renderHook(() => useMyHook());
expect(result.current.value).toBe(expected);
});
});import { describe, it, expect, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import { renderHook, act } from "@testing-library/react";
describe("MyComponent", () => {
it("renders correctly", () => {
render(<MyComponent />);
expect(screen.getByText("Hello")).toBeInTheDocument();
});
});
describe("useMyHook", () => {
it("returns expected value", () => {
const { result } = renderHook(() => useMyHook());
expect(result.current.value).toBe(expected);
});
});React.FCpnpm run lintpnpm run typecheckpnpm run testReact.FCpnpm run lintpnpm run typecheckpnpm run testreferences/best-practices.mdreferences/useeffect-patterns.mdreferences/best-practices.mdreferences/useeffect-patterns.md