Pixiv - SWKL:D
1877 字
9 分钟
React Hooks
React Hooks
React Hooks是React 16.8引入的新特性,允许你在函数组件中使用state和其他React特性,而不需要编写类组件。Hooks的出现使得函数组件的功能更加强大,代码更加简洁易读。
1. useState Hook
useState是最基本的Hook,用于在函数组件中添加state。
基本用法
import React, { useState } from 'react';
function Counter() { // 声明一个名为count的state变量,初始值为0 // setCount是更新count的函数 const [count, setCount] = useState(0);
return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> );}注意事项
useState接收一个初始值作为参数,返回一个数组,包含当前state值和更新state的函数- 初始值只在组件首次渲染时使用
- 调用更新函数时,React会重新渲染组件
- 可以在一个组件中使用多个
useStateHook
2. useEffect Hook
useEffect Hook用于在组件渲染后执行副作用操作,如数据获取、订阅或手动修改DOM。
基本用法
import React, { useState, useEffect } from 'react';
function Example() { const [count, setCount] = useState(0);
// 类似于componentDidMount和componentDidUpdate useEffect(() => { // 更新文档标题 document.title = `You clicked ${count} times`; });
return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> );}清理副作用
import React, { useState, useEffect } from 'react';
function FriendStatus(props) { const [isOnline, setIsOnline] = useState(null);
useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); }
// 订阅好友状态 ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// 清理函数,在组件卸载或重新渲染前执行 return function cleanup() { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; });
if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline';}控制副作用的执行时机
import React, { useState, useEffect } from 'react';
function Example() { const [count, setCount] = useState(0); const [name, setName] = useState('');
// 只在count变化时执行 useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // 依赖数组,只有当数组中的值变化时,才会重新执行副作用
return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> <input type="text" value={name} onChange={(e) => setName(e.target.value)} /> </div> );}- 依赖数组为空
[]时,副作用只在组件首次渲染时执行一次,类似于componentDidMount - 依赖数组中包含变量时,只有当这些变量变化时,副作用才会重新执行
- 不提供依赖数组时,副作用会在每次组件渲染后都执行
3. useContext Hook
useContext Hook用于在函数组件中访问React的Context API,避免了使用Context.Consumer。
基本用法
import React, { useContext } from 'react';
// 创建Contextconst ThemeContext = React.createContext('light');
function App() { return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> );}
function Toolbar() { return ( <div> <ThemedButton /> </div> );}
function ThemedButton() { // 使用useContext Hook访问Context const theme = useContext(ThemeContext); return ( <button style={{ background: theme === 'dark' ? '#333' : '#fff', color: theme === 'dark' ? '#fff' : '#333' }}> {theme} theme button </button> );}4. useReducer Hook
useReducer Hook是useState的替代方案,用于管理复杂的state逻辑。
基本用法
import React, { useReducer } from 'react';
// 定义reducer函数function reducer(state, action) { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; default: throw new Error(); }}
function Counter() { // 初始化state和dispatch函数 const [state, dispatch] = useReducer(reducer, { count: 0 });
return ( <div> <p>Count: {state.count}</p> <button onClick={() => dispatch({ type: 'increment' })}> Increment </button> <button onClick={() => dispatch({ type: 'decrement' })}> Decrement </button> </div> );}5. useCallback Hook
useCallback Hook用于缓存函数,避免在每次渲染时都创建新的函数实例。
基本用法
import React, { useState, useCallback } from 'react';
function Button({ onClick, children }) { console.log('Button rendered'); return <button onClick={onClick}>{children}</button>;}
function App() { const [count, setCount] = useState(0);
// 缓存handleClick函数,只有当count变化时才会重新创建 const handleClick = useCallback(() => { console.log('Clicked with count:', count); }, [count]);
return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}> Increment </button> <Button onClick={handleClick}> Click me </Button> </div> );}6. useMemo Hook
useMemo Hook用于缓存计算结果,避免在每次渲染时都重新计算。
基本用法
import React, { useState, useMemo } from 'react';
function App() { const [count, setCount] = useState(0); const [name, setName] = useState('');
// 缓存计算结果,只有当count变化时才会重新计算 const expensiveValue = useMemo(() => { console.log('Calculating expensive value...'); let result = 0; for (let i = 0; i < 1000000000; i++) { result += i; } return result; }, [count]);
return ( <div> <p>Count: {count}</p> <p>Expensive value: {expensiveValue}</p> <button onClick={() => setCount(count + 1)}> Increment </button> <input type="text" value={name} onChange={(e) => setName(e.target.value)} placeholder="Enter your name" /> </div> );}7. useRef Hook
useRef Hook用于创建一个可变的ref对象,其.current属性可以存储任意值,类似于类组件中的实例属性。
基本用法
import React, { useRef } from 'react';
function TextInputWithFocusButton() { // 创建一个ref对象 const inputEl = useRef(null);
// 聚焦输入框 const onButtonClick = () => { inputEl.current.focus(); };
return ( <div> <input ref={inputEl} type="text" /> <button onClick={onButtonClick}> Focus the input </button> </div> );}8. 自定义Hook
自定义Hook是一个函数,其名称以”use”开头,可以在其中使用其他Hook。自定义Hook允许你重用状态逻辑,而不需要复制粘贴代码。
基本用法
import React, { useState, useEffect } from 'react';
// 自定义Hookfunction useFriendStatus(friendId) { const [isOnline, setIsOnline] = useState(null);
useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); }
ChatAPI.subscribeToFriendStatus(friendId, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(friendId, handleStatusChange); }; }, [friendId]);
return isOnline;}
// 在组件中使用自定义Hookfunction FriendListItem(props) { const isOnline = useFriendStatus(props.friend.id);
return ( <li style={{ color: isOnline ? 'green' : 'black' }}> {props.friend.name} </li> );}
function FriendStatus(props) { const isOnline = useFriendStatus(props.friend.id);
if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline';}9. Hook的规则
使用Hook时需要遵循以下规则:
- 只在顶层调用Hook:不要在循环、条件或嵌套函数中调用Hook
- 只在React函数组件或自定义Hook中调用Hook:不要在普通JavaScript函数中调用Hook
- 遵循Hook的命名约定:自定义Hook的名称必须以”use”开头
10. 常用Hook组合
useState + useEffect
import React, { useState, useEffect } from 'react';
function DataFetcher({ url }) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null);
useEffect(() => { const fetchData = async () => { try { setLoading(true); const response = await fetch(url); const jsonData = await response.json(); setData(jsonData); setError(null); } catch (err) { setError(err.message); setData(null); } finally { setLoading(false); } };
fetchData(); }, [url]);
if (loading) { return <div>Loading...</div>; }
if (error) { return <div>Error: {error}</div>; }
return <div>Data: {JSON.stringify(data)}</div>;}useState + useCallback
import React, { useState, useCallback } from 'react';
function TodoList({ todos, onToggle, onDelete }) { return ( <ul> {todos.map(todo => ( <li key={todo.id}> <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }} onClick={() => onToggle(todo.id)} > {todo.text} </span> <button onClick={() => onDelete(todo.id)}> Delete </button> </li> ))} </ul> );}
function App() { const [todos, setTodos] = useState([ { id: 1, text: 'Learn React', completed: false }, { id: 2, text: 'Learn Hooks', completed: false }, { id: 3, text: 'Build an app', completed: false } ]);
// 缓存onToggle函数 const onToggle = useCallback((id) => { setTodos(todos.map(todo => todo.id === id ? { ...todo, completed: !todo.completed } : todo )); }, [todos]);
// 缓存onDelete函数 const onDelete = useCallback((id) => { setTodos(todos.filter(todo => todo.id !== id)); }, [todos]);
return ( <div> <TodoList todos={todos} onToggle={onToggle} onDelete={onDelete} /> </div> );}练习
- 使用useState和useEffect创建一个计数器组件,能够显示当前时间。
- 使用useContext创建一个主题切换组件,能够在明/暗主题之间切换。
- 使用useReducer创建一个待办事项列表,支持添加、删除和标记完成功能。
- 创建一个自定义Hook,用于处理表单输入。
总结
在本章节中,我们学习了React Hooks的使用方法和最佳实践,包括:
- useState:在函数组件中添加state
- useEffect:处理副作用操作
- useContext:访问React的Context API
- useReducer:管理复杂的state逻辑
- useCallback:缓存函数
- useMemo:缓存计算结果
- useRef:创建可变的ref对象
- 自定义Hook:重用状态逻辑
- Hook的规则:只在顶层调用、只在React函数组件或自定义Hook中调用
Hooks的出现使得函数组件的功能更加强大,代码更加简洁易读。在现代React开发中,Hooks已经成为主流,推荐使用函数组件配合Hooks来构建React应用。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
Lirael's Tech Firefly