上周折腾React花了两天时间,记录一下过程,下次再搞就不用重新查了。

安装

# 用 Vite 创建项目(比 CRA 快很多)
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run dev

# 常用依赖
npm install react-router-dom zustand axios

常用代码片段

// 自定义 Hook:防抖请求
import { useState, useEffect, useCallback } from "react";

function useDebounceSearch(delay = 300) {
  const [keyword, setKeyword] = useState("");
  const [results, setResults] = useState([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    if (!keyword.trim()) {
      setResults([]);
      return;
    }
    const timer = setTimeout(async () => {
      setLoading(true);
      try {
        const res = await fetch(\`/api/search?q=\${keyword}\`);
        const data = await res.json();
        setResults(data);
      } catch (e) {
        console.error("搜索失败", e);
      } finally {
        setLoading(false);
      }
    }, delay);
    return () => clearTimeout(timer);
  }, [keyword, delay]);

  return { keyword, setKeyword, results, loading };
}

// 使用
function SearchBox() {
  const { keyword, setKeyword, results, loading } = useDebounceSearch();
  return (
    <div>
      <input value={keyword} onChange={e => setKeyword(e.target.value)} placeholder="搜索..." />
      {loading && <span>加载中...</span>}
      <ul>{results.map(r => <li key={r.id}>{r.title}</li>)}</ul>
    </div>
  );
}

日常小技巧

  • useEffect 的依赖数组别偷懒写 [],该加的不加会导致 stale closure
  • key 用稳定 ID,别用 index,列表增删的时候会出 bug
  • React.memo 用在渲染成本高的组件上,别啥都 memo
  • 状态能往下放就往下放,别全塞 Context 里,一个更新全部重渲染

一个实际例子

之前一个列表页渲染5万条数据,直接卡死了。后来看了 Dan Abramov 的视频,用虚拟列表(react-window)只渲染可见区域,滚动丝滑得很。关键代码就几行。

常见问题

useEffect 无限循环怎么破?

90% 是依赖数组里放了对象或函数引用。把对象用 useMemo 包一层,函数用 useCallback。或者把依赖移到 effect 里面。

组件太大了怎么拆?

按职责拆:数据获取一个组件,展示一个组件,交互逻辑抽成自定义 Hook。一个组件超过 200 行就该考虑拆了。

React 和 Vue 怎么选?

别纠结,都能干活。团队熟悉哪个用哪个。React 生态大,Vue 上手快。要是做中后台,Vue + Element 可能更快出活。

先写到这,后面有新发现再更新。觉得有用的话收藏一下。