技术文档中心
首页
React
Vue
TypeScript
Kotlin
React Native
Electron
Android
首页
React
Vue
TypeScript
Kotlin
React Native
Electron
Android
  • 入门基础

    • React 学习指南
    • React 快速入门
    • 状态管理基础
    • Hooks 基础
    • 组件通信
    • 生命周期与副作用
    • 实战项目
  • 进阶提升

    • Hooks 进阶
    • 组件设计模式
    • 性能优化
    • React Router
    • 表单处理
    • HTTP 请求
  • 状态管理

    • Context API
    • Redux 状态管理
    • Zustand 轻量状态管理
  • 高级主题

    • React + TypeScript
    • React 测试
    • 服务端渲染 (SSR)
    • 微前端架构

生命周期与副作用

useEffect 详解

组件挂载

function Component() {
  useEffect(() => {
    console.log('组件已挂载');
    // 相当于 componentDidMount
  }, []); // 空依赖数组

  return <div>内容</div>;
}

组件更新

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    console.log('userId 变化了:', userId);
    fetchUser(userId).then(setUser);
  }, [userId]); // userId 变化时执行

  return <div>{user?.name}</div>;
}

组件卸载

function Timer() {
  useEffect(() => {
    const timer = setInterval(() => {
      console.log('tick');
    }, 1000);

    // 清理函数,组件卸载时执行
    return () => {
      console.log('组件卸载,清理定时器');
      clearInterval(timer);
    };
  }, []);

  return <div>定时器运行中</div>;
}

完整生命周期示例

function LifecycleDemo({ prop }) {
  const [state, setState] = useState(0);

  // 1. 挂载时执行一次
  useEffect(() => {
    console.log('组件挂载');
    return () => console.log('组件卸载');
  }, []);

  // 2. prop 变化时执行
  useEffect(() => {
    console.log('prop 变化:', prop);
  }, [prop]);

  // 3. state 变化时执行
  useEffect(() => {
    console.log('state 变化:', state);
  }, [state]);

  // 4. 每次渲染都执行(慎用)
  useEffect(() => {
    console.log('每次渲染');
  });

  return (
    <div>
      <button onClick={() => setState(state + 1)}>
        点击 ({state})
      </button>
    </div>
  );
}

常见副作用场景

数据获取

function UserList() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    let isMounted = true;

    fetch('/api/users')
      .then(res => res.json())
      .then(data => {
        if (isMounted) {
          setUsers(data);
          setLoading(false);
        }
      })
      .catch(err => {
        if (isMounted) {
          setError(err.message);
          setLoading(false);
        }
      });

    return () => {
      isMounted = false;
    };
  }, []);

  if (loading) return <div>加载中...</div>;
  if (error) return <div>错误: {error}</div>;
  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

订阅与取消订阅

function ChatRoom({ roomId }) {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const socket = connectToRoom(roomId);

    socket.on('message', (msg) => {
      setMessages(prev => [...prev, msg]);
    });

    return () => {
      socket.disconnect();
    };
  }, [roomId]);

  return (
    <div>
      {messages.map((msg, i) => (
        <div key={i}>{msg}</div>
      ))}
    </div>
  );
}

DOM 操作

function ScrollToTop() {
  useEffect(() => {
    window.scrollTo(0, 0);
  }, []);

  return <div>页面内容</div>;
}

function AutoFocus() {
  const inputRef = useRef(null);

  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  return <input ref={inputRef} />;
}

监听事件

function MouseTracker() {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    const handleMove = (e) => {
      setPosition({ x: e.clientX, y: e.clientY });
    };

    window.addEventListener('mousemove', handleMove);

    return () => {
      window.removeEventListener('mousemove', handleMove);
    };
  }, []);

  return <div>鼠标位置: {position.x}, {position.y}</div>;
}

定时器

function Countdown({ seconds }) {
  const [timeLeft, setTimeLeft] = useState(seconds);

  useEffect(() => {
    if (timeLeft <= 0) return;

    const timer = setTimeout(() => {
      setTimeLeft(timeLeft - 1);
    }, 1000);

    return () => clearTimeout(timer);
  }, [timeLeft]);

  return <div>{timeLeft > 0 ? `剩余 ${timeLeft} 秒` : '时间到!'}</div>;
}

useLayoutEffect

// 在浏览器重绘之前同步执行
function MeasureElement() {
  const [height, setHeight] = useState(0);
  const ref = useRef(null);

  useLayoutEffect(() => {
    // 读取 DOM 尺寸
    setHeight(ref.current.offsetHeight);
  }, []);

  return (
    <div ref={ref}>
      <p>元素高度: {height}px</p>
    </div>
  );
}

依赖数组最佳实践

// ❌ 错误:缺少依赖
useEffect(() => {
  console.log(count); // count 应该在依赖数组中
}, []);

// ✅ 正确
useEffect(() => {
  console.log(count);
}, [count]);

// ❌ 错误:依赖对象每次都是新的
useEffect(() => {
  fetchData(options);
}, [options]); // options 是对象,每次渲染都不同

// ✅ 正确:使用具体的值
useEffect(() => {
  fetchData(options);
}, [options.id, options.type]);
最近更新: 2026/1/27 15:51
Contributors: hailong
Prev
组件通信
Next
实战项目