当前位置:首页 > 未命名 > 正文内容

React Hooks最佳实践完全指南

廖万里9小时前未命名1

React Hooks概述

Hooks是React 16.8引入的新特性,允许在函数组件中使用状态和其他React特性,无需编写类组件。

为什么使用Hooks

  • 代码更简洁:无需class和this
  • 逻辑复用:自定义Hook轻松共享逻辑
  • 副作用分离:相关代码集中管理
  • 更好的类型推断:TypeScript友好

基础Hooks

useState

import { useState } from 'react';

function Counter() {
  // [当前状态, 更新函数]
  const [count, setCount] = useState(0);
  
  // 函数式更新(基于前一个状态)
  const increment = () => setCount(prev => prev + 1);
  
  // 惰性初始化(复杂初始值)
  const [data, setData] = useState(() => {
    const saved = localStorage.getItem('data');
    return saved ? JSON.parse(saved) : defaultValue;
  });
  
  return (
    

Count: {count}

); }

useEffect

import { useEffect, useState } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  
  // 组件挂载时执行
  useEffect(() => {
    console.log('Component mounted');
    
    return () => {
      console.log('Component unmounted');
    };
  }, []);
  
  // 依赖变化时执行
  useEffect(() => {
    let cancelled = false;
    
    async function fetchUser() {
      setLoading(true);
      const response = await fetch(`/api/users/${userId}`);
      const data = await response.json();
      
      if (!cancelled) {
        setUser(data);
        setLoading(false);
      }
    }
    
    fetchUser();
    
    // 清理函数
    return () => {
      cancelled = true;
    };
  }, [userId]); // 依赖数组
  
  if (loading) return 
Loading...
; return
{user?.name}
; }

useContext

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

// 创建Context
const ThemeContext = createContext('light');
const UserContext = createContext(null);

function App() {
  const [theme, setTheme] = useState('dark');
  const [user, setUser] = useState({ name: 'Alice' });
  
  return (
    
      
        
      
    
  );
}

function ThemeToggle() {
  const { theme, setTheme } = useContext(ThemeContext);
  
  return (
    
  );
}

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

性能优化Hooks

useMemo和useCallback

import { useMemo, useCallback, useState } from 'react';

function ExpensiveComponent({ items, onItemClick }) {
  // 缓存计算结果
  const sortedItems = useMemo(() => {
    console.log('Sorting items...');
    return [...items].sort((a, b) => a.name.localeCompare(b.name));
  }, [items]);
  
  // 缓存回调函数
  const handleClick = useCallback((id) => {
    console.log('Item clicked:', id);
    onItemClick(id);
  }, [onItemClick]);
  
  return (
    
    {sortedItems.map(item => ( ))}
); } // 避免过度优化 // ❌ 不需要useMemo const name = useMemo(() => user.name, [user]); // ✅ 复杂计算使用useMemo const total = useMemo(() => { return items.reduce((sum, item) => sum + item.price, 0); }, [items]);

React.memo

import { memo } from 'react';

// 浅比较props
const Item = memo(function Item({ item, onClick }) {
  return (
    
  • onClick(item.id)}> {item.name}
  • ); }); // 自定义比较函数 const ItemWithCustomCompare = memo( function Item({ item }) { return
  • {item.name}
  • ; }, (prevProps, nextProps) => { return prevProps.item.id === nextProps.item.id; } );

    自定义Hooks

    useLocalStorage

    function useLocalStorage(key, initialValue) {
      const [storedValue, setStoredValue] = useState(() => {
        try {
          const item = localStorage.getItem(key);
          return item ? JSON.parse(item) : initialValue;
        } catch (error) {
          console.error(error);
          return initialValue;
        }
      });
      
      const setValue = (value) => {
        try {
          const valueToStore = value instanceof Function 
            ? value(storedValue) 
            : value;
          setStoredValue(valueToStore);
          localStorage.setItem(key, JSON.stringify(valueToStore));
        } catch (error) {
          console.error(error);
        }
      };
      
      return [storedValue, setValue];
    }
    
    // 使用
    const [name, setName] = useLocalStorage('name', 'Guest');
    

    useFetch

    function useFetch(url) {
      const [data, setData] = useState(null);
      const [loading, setLoading] = useState(true);
      const [error, setError] = useState(null);
      
      useEffect(() => {
        let cancelled = false;
        
        async function fetchData() {
          setLoading(true);
          try {
            const response = await fetch(url);
            if (!response.ok) throw new Error('Network error');
            const json = await response.json();
            if (!cancelled) {
              setData(json);
              setError(null);
            }
          } catch (e) {
            if (!cancelled) setError(e);
          } finally {
            if (!cancelled) setLoading(false);
          }
        }
        
        fetchData();
        
        return () => { cancelled = true; };
      }, [url]);
      
      return { data, loading, error };
    }
    
    // 使用
    function UserList() {
      const { data: users, loading, error } = useFetch('/api/users');
      
      if (loading) return 
    Loading...
    ; if (error) return
    Error: {error.message}
    ; return
      {users.map(u =>
    • {u.name}
    • )}
    ; }

    进阶Hooks

    useReducer

    import { useReducer } from 'react';
    
    const initialState = {
      count: 0,
      history: []
    };
    
    function reducer(state, action) {
      switch (action.type) {
        case 'increment':
          return {
            count: state.count + 1,
            history: [...state.history, state.count]
          };
        case 'decrement':
          return {
            count: state.count - 1,
            history: [...state.history, state.count]
          };
        case 'reset':
          return initialState;
        default:
          throw new Error('Unknown action');
      }
    }
    
    function Counter() {
      const [state, dispatch] = useReducer(reducer, initialState);
      
      return (
        

    Count: {state.count}

    ); }

    useRef

    import { useRef, useEffect } from 'react';
    
    function TextInputWithFocus() {
      const inputRef = useRef(null);
      const renderCount = useRef(0);
      
      useEffect(() => {
        renderCount.current += 1;
      });
      
      const focusInput = () => {
        inputRef.current?.focus();
      };
      
      return (
        

    Renders: {renderCount.current}

    ); }

    最佳实践

    1. 遵循Hooks规则:只在顶层调用,不在循环/条件中
    2. 正确设置依赖:使用ESLint插件检查
    3. 合理拆分Effect:每个Effect做一件事
    4. 避免过度优化:不是所有函数都需要useCallback
    5. 抽象自定义Hook:复用逻辑,保持组件简洁

    React Hooks让函数组件拥有了完整的能力,掌握它是现代React开发的必备技能。

    React Hooks React函数式组件新时代

    本文链接:https://www.kkkliao.cn/?id=758 转载需授权!

    分享到:

    版权声明:本文由廖万里的博客发布,如需转载请注明出处。


    发表评论

    访客

    看不清,换一张

    ◎欢迎参与讨论,请在这里发表您的看法和观点。