React / 闯关模式

React 核心篇:Hooks 表单 请求 用户列表

用 Hooks 管搜索、表单、请求、loading、error,把用户列表变成真实业务页面。

一句话:React 核心能力就是用 Hooks 管状态、管副作用、管表单和接口请求,把静态组件变成真实业务页面。

第 3 篇 / 共 12 篇。

本篇学完你会:用 React 19 + Hooks + TypeScript 写用户列表的搜索、请求、loading、error、新增编辑表单。

1. Hooks 解决什么问题

Hooks 是 React 函数组件里使用“组件能力”的方式。

Hook大白话
useState记住一份会变化的数据
useEffect在渲染之后做副作用,比如请求接口
useMemo缓存计算结果
useCallback缓存函数引用
useRef记住一个不会触发重渲染的值

React Hooks 用户列表请求流程

2. useState 管表单

搜索条件就是一份 state:

type UserQuery = {
  keyword: string;
  status: 'all' | 'active' | 'disabled';
};

const [query, setQuery] = useState<UserQuery>({
  keyword: '',
  status: 'all'
});

更新某一个字段时,不要丢掉其他字段:

setQuery((current) => ({
  ...current,
  keyword: event.target.value
}));

大白话:表单 state 像一张草稿纸,每次改一个字段,都要把整张草稿纸交回给 React。

3. useEffect 管接口请求

接口请求属于副作用。它不是单纯算 UI,而是去外部世界拿数据。

useEffect(() => {
  async function loadUsers() {
    const response = await fetch('/api/users');
    const data = await response.json();
    setUsers(data);
  }

  loadUsers();
}, []);

第二个参数 [] 表示这个 effect 只在组件挂载后跑一次。

如果搜索条件变化就重新请求:

useEffect(() => {
  loadUsers(query);
}, [query]);

4. 用户列表请求流程

真实页面不能只管成功,还要管 loading 和 error。

type UserState = {
  data: User[];
  loading: boolean;
  error: string | null;
};

const [userState, setUserState] = useState<UserState>({
  data: [],
  loading: false,
  error: null
});

请求时:

async function loadUsers(query: UserQuery) {
  setUserState((current) => ({ ...current, loading: true, error: null }));

  try {
    const search = new URLSearchParams({
      keyword: query.keyword,
      status: query.status
    });
    const response = await fetch(`/api/users?${search.toString()}`);

    if (!response.ok) {
      throw new Error('用户列表加载失败');
    }

    const data = (await response.json()) as User[];
    setUserState({ data, loading: false, error: null });
  } catch (error) {
    setUserState({
      data: [],
      loading: false,
      error: error instanceof Error ? error.message : '未知错误'
    });
  }
}

页面渲染:

{userState.loading && <p>加载中...</p>}
{userState.error && <p>{userState.error}</p>}
{!userState.loading && !userState.error && <UserList users={userState.data} />}

5. 新增和编辑表单

新增和编辑可以共用一个表单组件:

type UserFormValue = {
  name: string;
  email: string;
  role: 'admin' | 'member';
  status: 'active' | 'disabled';
};

type UserFormProps = {
  initialValue: UserFormValue;
  onSubmit: (value: UserFormValue) => Promise<void>;
};

表单组件内部管理草稿:

function UserForm({ initialValue, onSubmit }: UserFormProps) {
  const [value, setValue] = useState(initialValue);
  const [submitting, setSubmitting] = useState(false);

  async function handleSubmit(event: React.FormEvent) {
    event.preventDefault();
    setSubmitting(true);
    await onSubmit(value);
    setSubmitting(false);
  }

  return (
    <form onSubmit={handleSubmit}>
      <input value={value.name} onChange={(event) => setValue({ ...value, name: event.target.value })} />
      <input value={value.email} onChange={(event) => setValue({ ...value, email: event.target.value })} />
      <button disabled={submitting}>{submitting ? '保存中...' : '保存'}</button>
    </form>
  );
}

6. useMemo 和 useCallback 什么时候用

不要一上来就乱用。

适合 useMemo 的情况:

const activeUsers = useMemo(() => users.filter((user) => user.status === 'active'), [users]);

适合 useCallback 的情况:

const handleDelete = useCallback(async (id: number) => {
  await deleteUser(id);
  await loadUsers(query);
}, [query]);

大白话:只有当计算比较贵、函数会传给子组件、或者依赖稳定性真的重要时再用。

7. 自定义 Hook

用户列表逻辑可以抽成 useUsers

function useUsers() {
  const [query, setQuery] = useState<UserQuery>({ keyword: '', status: 'all' });
  const [state, setState] = useState<UserState>({ data: [], loading: false, error: null });

  const reload = useCallback(() => loadUsers(query), [query]);

  useEffect(() => {
    reload();
  }, [reload]);

  return { query, setQuery, state, reload };
}

这样页面组件就更像在描述布局:

const { query, setQuery, state, reload } = useUsers();

8. 常见错误

错误后果修正
useEffect 依赖漏写页面用旧数据按规则补依赖
请求没有 loading用户不知道发生了什么增加 loading 状态
请求失败不处理页面静默坏掉增加 error 状态
表单直接改 props数据来源混乱用本地草稿 state
过早 useMemo代码变复杂先写清楚,再优化

9. 下一步怎么学

本篇重点是把用户管理页从“能显示”推进到“能交互”。

能力对应任务
useState搜索条件、表单草稿
useEffect加载用户列表
loading/error真实请求体验
自定义 Hook抽出业务逻辑
表单组件新增和编辑用户

下一篇建议:大白话讲解——React 进阶篇:路由 状态管理 权限 工程化.md