概述
React Hooks 是 React 16.8 引入的新特性,允許在函數組件中使用狀態和其他 React 特性。根據數據的使用場景和更新機制,可以將 Hooks 分為三大類:
1. 保存只讀數據
useMemo
用途:?緩存計算結果,避免重復計算
語法:
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
特點:
- 只有當依賴項發生變化時才重新計算
- 適用于計算量大的操作
- 返回緩存的值
使用場景:
// 復雜計算
const expensiveValue = useMemo(() => {return items.filter((item) => item.price > 100).map((item) => ({...item,discount: item.price * 0.1,}));
}, [items]);// 對象/數組緩存
const memoizedObject = useMemo(() => ({id: user.id,name: user.name,avatar: user.avatar,}),
[user.id, user.name, user.avatar]
);
useCallback
用途:?緩存函數,避免子組件不必要的重新渲染
語法:
const memoizedCallback = useCallback(() => {doSomething(a, b);
}, [a, b]);
特點:
- 只有當依賴項變化時才創建新的函數
- 主要用于性能優化
- 返回緩存的函數引用
使用場景:
// 傳遞給子組件的回調函數
const handleClick = useCallback((id) => {setSelectedId(id);fetchUserDetails(id);
}, []);// 事件處理函數
const handleSubmit = useCallback((formData) => {submitForm(formData);},[submitForm]
);// 子組件優化
const ChildComponent = React.memo(({ onUpdate }) => {return <button onClick={onUpdate}>更新</button>;
});
2. 保存可變數據,更改時觸發渲染
useState
用途:?管理組件內的簡單狀態
語法:
const [state, setState] = useState(initialState);
特點:
- 狀態更新會觸發組件重新渲染
- 異步更新,多個 setState 會被批處理
- 適合簡單的狀態管理
使用場景:
// 基礎狀態管理
const [count, setCount] = useState(0);
const [name, setName] = useState("");
const [isLoading, setIsLoading] = useState(false);// 對象狀態
const [user, setUser] = useState({name: "",email: "",age: 0,
});// 函數式更新
setCount((prevCount) => prevCount + 1);
setUser((prevUser) => ({...prevUser,name: "新名字",
}));
useReducer
用途:?管理復雜的狀態邏輯
語法:
const [state, dispatch] = useReducer(reducer, initialState, init);
特點:
- 狀態更新邏輯集中管理
- 適合復雜的狀態轉換
- 可以處理多個相關的狀態
使用場景:
// 定義 reducer
const todoReducer = (state, action) => {switch (action.type) {case "ADD_TODO":return {...state,todos: [...state.todos, action.payload],};case "TOGGLE_TODO":return {...state,todos: state.todos.map((todo) =>todo.id === action.payload? { ...todo, completed: !todo.completed }: todo),};case "DELETE_TODO":return {...state,todos: state.todos.filter((todo) => todo.id !== action.payload),};default:return state;}
};// 使用 useReducer
const [state, dispatch] = useReducer(todoReducer, {todos: [],filter: "all",
});// 分發 action
dispatch({ type: "ADD_TODO", payload: { id: 1, text: "學習 React" } });
dispatch({ type: "TOGGLE_TODO", payload: 1 });
3. 保存可變數據,更改時不觸發渲染
useRef
用途:?保存可變值,不觸發重新渲染
語法:
const refContainer = useRef(initialValue);
特點:
- 值的變化不會觸發組件重新渲染
- 可以訪問 DOM 元素
- 適合存儲不需要觸發渲染的數據
使用場景:
// 訪問 DOM 元素
const inputRef = useRef(null);
const focusInput = () => {inputRef.current.focus();
};// 存儲定時器 ID
const timerRef = useRef(null);
useEffect(() => {timerRef.current = setInterval(() => {console.log("定時器執行");}, 1000);return () => {if (timerRef.current) {clearInterval(timerRef.current);}};
}, []);// 存儲前一次的值
const prevCountRef = useRef();
useEffect(() => {prevCountRef.current = count;
});// 存儲實例變量
const instanceRef = useRef({isMounted: false,data: null,
});
最佳實踐
性能優化
// 1. 合理使用 useMemo 和 useCallback
const expensiveValue = useMemo(() => {return heavyComputation(data);
}, [data]);const handleClick = useCallback(() => {// 處理點擊事件
}, [dependencies]);// 2. 避免在渲染中創建對象/函數
// ? 錯誤做法
const Component = () => {const [count, setCount] = useState(0);// 每次渲染都會創建新對象const style = { color: "red" };const handleClick = () => setCount(count + 1);return (<div style={style} onClick={handleClick}>{count}</div>);
};// ? 正確做法
const Component = () => {const [count, setCount] = useState(0);const style = useMemo(() => ({ color: "red" }), []);const handleClick = useCallback(() => setCount((prev) => prev + 1), []);return (<div style={style} onClick={handleClick}>{count}</div>);
};
狀態管理選擇
// 簡單狀態 - 使用 useState
const [isVisible, setIsVisible] = useState(false);
const [userName, setUserName] = useState("");// 復雜狀態 - 使用 useReducer
const [formState, dispatch] = useReducer(formReducer, {fields: {},errors: {},isValid: false,
});// 不需要渲染的數據 - 使用 useRef
const previousValue = useRef(null);
const timeoutId = useRef(null);
依賴項管理
// 1. 正確設置依賴項
useEffect(() => {fetchData(userId);
}, [userId]); // 包含所有依賴項// 2. 使用 useCallback 避免依賴項變化
const fetchData = useCallback((id) => {// 獲取數據邏輯
}, []); // 空依賴數組// 3. 使用 useRef 存儲最新值
const latestValue = useRef(value);
useEffect(() => {latestValue.current = value;
});useEffect(() => {const timer = setInterval(() => {console.log(latestValue.current); // 總是獲取最新值}, 1000);return () => clearInterval(timer);
}, []); // 不需要依賴 value
常見陷阱
1. 過度優化
// ? 不必要的 useMemo
const simpleValue = useMemo(() => count + 1, [count]);// ? 直接計算
const simpleValue = count + 1;
2. 依賴項遺漏
// ? 遺漏依賴項
useEffect(() => {console.log(count);
}, []); // 缺少 count 依賴// ? 正確設置依賴項
useEffect(() => {console.log(count);
}, [count]);
3. 閉包陷阱
// ? 閉包陷阱
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {console.log(count); // 總是打印初始值
}, []); // 空依賴數組// ? 解決方案 1: 添加依賴項
const handleClick = useCallback(() => {console.log(count);
}, [count]);// ? 解決方案 2: 使用函數式更新
const handleClick = useCallback(() => {setCount((prevCount) => {console.log(prevCount);return prevCount + 1;});
}, []);
總結
選擇合適的 Hook 取決于具體的使用場景:
- useMemo/useCallback: 用于性能優化,緩存計算結果和函數
- useState/useReducer: 用于狀態管理,狀態變化會觸發重新渲染
- useRef: 用于存儲不需要觸發渲染的可變數據
合理使用這些 Hook 可以提升組件性能,避免不必要的重新渲染,同時保持代碼的可讀性和可維護性。
?React Hooks 完全指南:從基礎到高級的實戰技巧 - 高質量源碼分享平臺-免費下載各類網站源碼與模板及前沿技術分享