返回教程列表
教程 React 快速入门 第 4 节
进阶
25 分钟

React Hooks 详解

深入理解 React Hooks,掌握 useEffect、useContext 等常用 Hooks

CodeGogo

useEffect Hook

useEffect 用于处理副作用,如数据获取、订阅、手动修改 DOM 等。

基本用法

useEffect(() => {
  // 副作用代码
  document.title = `Count: ${count}`;

  // 清理函数(可选)
  return () => {
    // 清理操作
  };
}, [dependencies]); // 依赖数组

无依赖(每次渲染后执行)

useEffect(() => {
  console.log('组件渲染后执行');
});

空依赖数组(仅挂载时执行一次)

useEffect(() => {
  console.log('组件挂载时执行');

  return () => {
    console.log('组件卸载时执行');
  };
}, []);

有依赖(依赖变化时执行)

useEffect(() => {
  console.log('count 变化:', count);
}, [count]);

数据获取示例

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

  useEffect(() => {
    // 定义异步函数
    const fetchUser = async () => {
      try {
        setLoading(true);
        const response = await fetch(`/api/users/${userId}`);
        const data = await response.json();
        setUser(data);
      } catch (error) {
        console.error('Error:', error);
      } finally {
        setLoading(false);
      }
    };

    fetchUser();
  }, [userId]);

  if (loading) return <div>加载中...</div>;
  if (!user) return <div>用户不存在</div>;

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

useContext Hook

用于跨组件层级共享数据,避免 prop drilling。

创建 Context

import { createContext, useContext, useState } from 'react';

// 创建 Context
const ThemeContext = createContext();

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

// 自定义 Hook
function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within ThemeProvider');
  }
  return context;
}

使用 Context

function App() {
  return (
    <ThemeProvider>
      <Toolbar />
      <Content />
    </ThemeProvider>
  );
}

function Toolbar() {
  const { theme, setTheme } = useTheme();

  return (
    <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
      切换到{theme === 'light' ? '深色' : '浅色'}模式
    </button>
  );
}

useReducer Hook

用于管理复杂的状态逻辑。

import { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    case 'reset':
      return initialState;
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>
        +1
      </button>
      <button onClick={() => dispatch({ type: 'decrement' })}>
        -1
      </button>
      <button onClick={() => dispatch({ type: 'reset' })}>
        重置
      </button>
    </div>
  );
}

useMemo 和 useCallback

用于性能优化。

useMemo(缓存计算结果)

function ExpensiveComputation({ a, b }) {
  const result = useMemo(() => {
    console.log('执行复杂计算...');
    return a * b + Math.sqrt(a + b);
  }, [a, b]);

  return <div>结果: {result}</div>;
}

useCallback(缓存函数)

function ParentComponent() {
  const [count, setCount] = useState(0);

  // 不使用 useCallback(每次渲染都创建新函数)
  const handleClick = () => {
    setCount(count + 1);
  };

  // 使用 useCallback(只在依赖变化时创建新函数)
  const memoizedHandleClick = useCallback(() => {
    setCount(c => c + 1);
  }, []);

  return <ChildComponent onClick={memoizedHandleClick} />;
}

💡 最佳实践

  1. 依赖数组要完整:列出所有外部使用的变量
  2. 正确清理副作用:返回清理函数避免内存泄漏
  3. 合理使用 Context:只用于真正需要全局共享的状态
  4. 性能优化要适度:过早优化是万恶之源

🎯 练习

  1. 创建一个自动保存的表单组件
  2. 实现一个主题切换功能
  3. 使用 useReducer 重构一个复杂的状态逻辑

下一节我们将学习 事件处理和表单

觉得这个教程有帮助?

继续学习更多教程,掌握编程技能

查看更多教程