React Hooks入门与实践
React Hooks是React 16.8版本引入的新特性,它允许你在不编写class的情况下使用state以及其他的React特性。本文将从基础概念开始,逐步深入到自定义Hooks的创建和使用。
什么是React Hooks?
Hooks是一些可以让你在函数组件里"钩入"React state及生命周期等特性的函数。Hooks不能在class组件中使用,这让你不使用class也能使用React。
基本Hooks
useState
useState是最基本的Hook,它让你能够在函数组件中添加state。
import React, { useState } from 'react';
function Counter() {
// 声明一个新的state变量,我们将其称为"count"
const [count, setCount] = useState(0);
return (
你点击了 {count} 次
);
}
useState接收一个初始state值作为参数,并返回一个数组,其中第一个元素是当前的state值,第二个元素是一个更新state的函数。
useEffect
useEffect让你可以在函数组件中执行副作用操作(如数据获取、订阅或手动更改DOM)。
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// 相当于componentDidMount和componentDidUpdate
useEffect(() => {
// 更新文档标题
document.title = `你点击了 ${count} 次`;
// 可选的清理函数
return () => {
// 在组件卸载或重新渲染前执行清理
// 这里可以清除定时器、取消订阅等
};
}, [count]); // 仅在count改变时重新执行effect
return (
你点击了 {count} 次
);
}
useEffect接收两个参数:一个函数和一个依赖数组。依赖数组是可选的,如果省略,effect会在每次渲染后都执行;如果提供,effect只会在依赖项发生变化时执行。
useContext
useContext让你可以在组件中访问React的Context,无需通过组件树逐层传递props。
import React, { useContext } from 'react';
// 创建一个Context
const ThemeContext = React.createContext('light');
function ThemedButton() {
// 使用useContext访问Context
const theme = useContext(ThemeContext);
return (
);
}
function App() {
return (
);
}
额外的Hooks
useReducer
useReducer是useState的替代方案,用于管理复杂的state逻辑。
import React, { useReducer } from 'react';
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() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
Count: {state.count}
);
}
useMemo
useMemo用于性能优化,它可以缓存计算结果,避免在每次渲染时重复计算。
import React, { useState, useMemo } from 'react';
function ExpensiveComponent({ a, b }) {
// 只有当a或b改变时才会重新计算
const result = useMemo(() => {
console.log('执行昂贵的计算');
return a * b;
}, [a, b]);
return 计算结果: {result};
}
useCallback
useCallback与useMemo类似,但它缓存的是函数本身,而不是计算结果。
import React, { useState, useCallback } from 'react';
function Button({ onClick }) {
console.log('Button重新渲染');
return ;
}
function Parent() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
// 只有当count改变时,onClick函数才会重新创建
const onClick = useCallback(() => {
console.log(`Count: ${count}`);
}, [count]);
return (
setText(e.target.value)} />
Count: {count}
);
}
自定义Hooks
自定义Hooks允许你将组件逻辑提取到可重用的函数中。自定义Hook是一个函数,其名称以"use"开头,可以在其中调用其他Hook。
创建自定义Hook
import { useState, useEffect } from 'react';
function useWindowSize() {
// 声明state来存储窗口尺寸
const [windowSize, setWindowSize] = useState({
width: undefined,
height: undefined,
});
useEffect(() => {
// 处理窗口尺寸变化的函数
function handleResize() {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
// 添加事件监听器
window.addEventListener('resize', handleResize);
// 初始化
handleResize();
// 清理函数
return () => window.removeEventListener('resize', handleResize);
}, []); // 空依赖数组意味着这个effect只会在组件挂载和卸载时运行
return windowSize;
}
// 使用自定义Hook
function App() {
const size = useWindowSize();
return (
窗口宽度: {size.width}
窗口高度: {size.height}
);
}
自定义Hook案例:数据获取
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
// 重置状态
setLoading(true);
setError(null);
// 执行fetch
fetch(url)
.then(response => {
if (!response.ok) {
throw new Error('网络请求失败');
}
return response.json();
})
.then(data => {
setData(data);
setLoading(false);
})
.catch(error => {
setError(error);
setLoading(false);
});
}, [url]);
return { data, loading, error };
}
// 使用数据获取Hook
function UserList() {
const { data: users, loading, error } = useFetch('https://api.example.com/users');
if (loading) return 加载中...;
if (error) return 错误: {error.message};
return (
{users.map(user => (
- {user.name}
))}
);
}
Hook使用规则
使用React Hooks时需要遵循两条重要规则:
- 只在函数组件的顶层调用Hook:不要在循环、条件或嵌套函数中调用Hook
- 只在React函数组件或自定义Hook中调用Hook:不要在普通JavaScript函数中调用Hook
总结
React Hooks为函数组件提供了强大的功能,使我们能够在不使用class的情况下编写具有状态和副作用的组件。通过组合使用内置Hooks和创建自定义Hooks,我们可以构建更加模块化、可重用和易于测试的React应用程序。
在实际开发中,建议尽可能使用Hooks来编写组件,因为它们通常会使代码更加简洁和易于理解。同时,也要注意遵循Hooks的使用规则,以避免潜在的问题。