首页 / React 19 入门教程 / 你可能不需要 Effect

React 19 入门教程

你可能不需要 Effect

本教程共 50 篇 · 第 29 篇 · 更新于 2026-07-29 · 约 7 分钟阅读

ReactuseEffect派生状态状态重置最佳实践

29. 你可能不需要 Effect

本节目标:识别不必要的 effect,学会用更简单的方式替代,让代码更简洁、更不容易出错。

Effect 是”脱围机制”

React 官方文档里有一句很精辟的话:Effect 是 React 范式的一种脱围机制。

意思是:正常的 React 流程是 props → state → UI。当你需要”跳出”这个流程,与外部系统交互时,才用 Effect。

如果你的 effect 只是在 React 内部打转(根据 state 更新 state),大概率不需要。

情况一:能算就别存

根据现有 state 算出来的值,不要存 state,更不要用 effect 更新:

// 不好:多余的 state + effect
function Form() {
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  const [fullName, setFullName] = useState('');

  useEffect(() => {
    setFullName(firstName + ' ' + lastName);
  }, [firstName, lastName]);

  return <p>{fullName}</p>;
}

// 好:直接计算
function Form() {
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  const fullName = firstName + ' ' + lastName;

  return <p>{fullName}</p>;
}

判断标准

如果一个值能从 props 或 state 算出来,它就不应该是 state。

情况二:转换渲染数据

渲染前要筛选/排序列表,在组件顶层直接算:

// 不好:用 effect 存筛选结果
function TodoList({ todos, filter }) {
  const [visibleTodos, setVisibleTodos] = useState([]);

  useEffect(() => {
    setVisibleTodos(todos.filter(t => t.status === filter));
  }, [todos, filter]);

  return <ul>{visibleTodos.map(...)}</ul>;
}

// 好:渲染时直接算
function TodoList({ todos, filter }) {
  const visibleTodos = todos.filter(t => t.status === filter);
  return <ul>{visibleTodos.map(...)}</ul>;
}

如果筛选计算很贵,用 useMemo 缓存结果,但也不是 effect。

情况三:props 变化时重置 state

用 effect 根据 props 重置 state,不如用 key:

// 不好:用 effect 重置
function ProfilePage({ userId }) {
  const [comment, setComment] = useState('');

  useEffect(() => {
    setComment(''); // userId 变化时重置
  }, [userId]);
}

// 好:用 key 重置
function ProfilePage({ userId }) {
  return <Profile userId={userId} key={userId} />;
}

function Profile({ userId }) {
  const [comment, setComment] = useState('');
  // userId 变化时,key 变化,组件自动重置
}

key 重置更简洁,而且能重置组件内所有 state,不用一个个写。

情况四:处理用户事件

用户事件(点击、提交)的处理逻辑放在事件处理函数里,不要放在 effect 里:

// 不好:用 effect 响应事件
function Cart() {
  const [cart, setCart] = useState([]);

  useEffect(() => {
    if (cart.length > 0) {
      post('/api/cart', { items: cart });
    }
  }, [cart]); // cart 变化就发请求
}

// 好:在点击事件里发请求
function Cart() {
  const [cart, setCart] = useState([]);

  function handleCheckout() {
    post('/api/cart', { items: cart });
  }

  return <button onClick={handleCheckout}>结算</button>;
}

确实需要 Effect 的场景

以下场景才真正需要 effect:

  • 连接/断开外部服务(WebSocket、聊天室)
  • 订阅浏览器事件(resize、scroll、online)
  • 操作第三方 DOM 库(地图、图表)
  • 发送分析日志(组件曝光时)
  • 同步到 localStorage

一个简单的判断

问自己:这个操作是在和”外部系统”交互吗?如果不是,大概率不需要 effect。

移除不必要的 Effect 清单

  • 能算出来的值 → 直接计算,不存 state
  • 渲染前转换数据 → 在组件顶层算
  • props 变化重置 state → 用 key
  • 响应用户操作 → 放在事件处理函数
  • 初始化第三方库 → 可以,但要清理
  • 订阅外部事件 → 可以,但要清理

一个综合例子

// 之前:到处都是 effect
function SearchBox({ defaultQuery }) {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);

  // 不必要的 effect:初始化
  useEffect(() => {
    setQuery(defaultQuery);
  }, [defaultQuery]);

  // 不必要的 effect:派生数据
  useEffect(() => {
    setResults(filterItems(query));
  }, [query]);

  return (/* ... */);
}

// 之后:干净多了
function SearchBox({ defaultQuery }) {
  const [query, setQuery] = useState(defaultQuery);
  const results = filterItems(query);

  return (/* ... */);
}

本节要点

  • Effect 是脱围机制,不是常规工具
  • 能算出来的值不存 state
  • props 变化重置 state 用 key
  • 用户事件放在事件处理函数
  • 真正需要 effect 的是与外部系统交互