React Hooks 的优势和使用场景

88 阅读3分钟

React Hooks 的优势和使用场景

核心优势

  1. 简化组件逻辑
    • 告别 class 组件的繁琐生命周期
    • 将相关逻辑聚合到单个 Hook 中
    • 消除 this 绑定问题
// 类组件
class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return <button onClick={this.handleClick}>点击 {this.state.count} 次</button>;
  }
}

// 函数组件 + Hooks
function Example() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>点击 {count} 次</button>;
}
  1. 逻辑复用
    • 自定义 Hook 实现跨组件逻辑复用
    • 替代高阶组件和 render props 模式
// 自定义 Hook
function useWindowSize() {
  const [size, setSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight
  });

  useEffect(() => {
    const handleResize = () => setSize({
      width: window.innerWidth,
      height: window.innerHeight
    });
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return size;
}

// 在组件中使用
function MyComponent() {
  const { width } = useWindowSize();
  return <div>窗口宽度: {width}px</div>;
}
  1. 性能优化
    • 细粒度的状态更新控制
    • 避免不必要的渲染
function ExpensiveComponent() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState('');

  // 只有 count 变化时才重新计算
  const computedValue = useMemo(() => {
    return expensiveCalculation(count);
  }, [count]);

  // 只有在组件挂载时执行
  useEffect(() => {
    fetchInitialData();
  }, []);

  return (
    <div>
      <p>{computedValue}</p>
      <input value={text} onChange={e => setText(e.target.value)} />
      <button onClick={() => setCount(count + 1)}>增加</button>
    </div>
  );
}

主要使用场景

  1. 状态管理
    • useState: 基础状态管理
    • useReducer: 复杂状态逻辑
function TodoApp() {
  const [todos, dispatch] = useReducer(todoReducer, []);

  function handleAddTodo(text) {
    dispatch({ type: 'ADD_TODO', text });
  }

  // ...
}
  1. 副作用处理
    • useEffect: 数据获取、订阅、手动 DOM 操作
    • useLayoutEffect: DOM 变更后同步执行
function DataFetcher({ id }) {
  const [data, setData] = useState(null);

  useEffect(() => {
    let ignore = false;
    
    async function fetchData() {
      const result = await fetch(`/api/data/${id}`);
      if (!ignore) {
        setData(await result.json());
      }
    }

    fetchData();
    
    return () => { ignore = true; };
  }, [id]); // id 变化时重新获取

  // ...
}
  1. 性能优化
    • useMemo: 缓存计算结果
    • useCallback: 缓存函数引用
function Parent() {
  const [count, setCount] = useState(0);
  
  // 避免子组件不必要的重渲染
  const handleClick = useCallback(() => {
    setCount(c => c + 1);
  }, []);

  return (
    <>
      <Child onClick={handleClick} />
      <div>计数: {count}</div>
    </>
  );
}
  1. 访问 DOM 元素
    • useRef: 获取 DOM 引用或保存可变值
function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  
  const onButtonClick = () => {
    inputEl.current.focus();
  };

  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>聚焦输入框</button>
    </>
  );
}
  1. 上下文访问
    • useContext: 简化上下文使用
const ThemeContext = React.createContext('light');

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button style={{ background: theme === 'dark' ? '#333' : '#eee' }}>按钮</button>;
}

最佳实践

  1. Hook 使用规则

    • 只在 React 函数组件顶层调用 Hook
    • 不要在循环、条件或嵌套函数中调用 Hook
    • 自定义 Hook 必须以 "use" 开头
  2. 组织代码

    • 将复杂逻辑拆分为多个小 Hook
    • 相关逻辑组织在一起
  3. 性能考量

    • 合理使用依赖数组
    • 避免不必要的 effect 执行
    • 大型列表考虑虚拟化
  4. 测试策略

    • 使用 @testing-library/react-hooks 测试自定义 Hook
    • 模拟依赖项进行隔离测试

常见问题解决方案

  1. 无限循环

    // 错误示例
    useEffect(() => {
      setCount(count + 1); // 会导致无限循环
    }, [count]);
    
    // 正确方式
    useEffect(() => {
      setCount(c => c + 1); // 使用函数式更新
    }, []); // 空依赖数组
    
  2. 过时闭包

    function Counter() {
      const [count, setCount] = useState(0);
    
      useEffect(() => {
        const id = setInterval(() => {
          setCount(count + 1); // 总是使用初始值
        }, 1000);
        return () => clearInterval(id);
      }, []); // 缺少 count 依赖
    
      // 正确方式
      useEffect(() => {
        const id = setInterval(() => {
          setCount(c => c + 1); // 使用函数式更新
        }, 1000);
        return () => clearInterval(id);
      }, []);
    }
    
  3. 条件执行

    // 错误示例
    if (condition) {
      useEffect(() => { ... }); // 违反 Hook 规则
    }
    
    // 正确方式
    useEffect(() => {
      if (condition) {
        // 在 effect 内部进行条件判断
      }
    }, [condition]);
    

React Hooks 通过简化组件逻辑、提高代码复用性和优化性能,已经成为现代 React 开发的标准方式。合理运用各种 Hook 可以显著提升开发效率和代码可维护性。