在React中,自定义Hooks是一种强大的工具,允许你将组件逻辑提取为可重用的函数。以下是自定义Hooks的核心概念和实现示例:
自定义Hooks的核心要点
- 命名规则 :必须以
use
开头(如useFetch
),以便React识别并应用Hooks规则。 - 逻辑复用:封装状态管理、副作用等逻辑,避免代码重复。
- 组合Hooks :内部可使用
useState
、useEffect
等React内置Hooks。 - 独立状态:每次调用Hook会创建独立的状态,与调用它的组件实例绑定。
示例1:数据获取Hook(useFetch
)
处理异步请求,管理加载状态和错误。
jsx
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const abortController = new AbortController();
fetch(url, { signal: abortController.signal })
.then((res) => {
if (!res.ok) throw new Error('请求失败');
return res.json();
})
.then((data) => {
setData(data);
setLoading(false);
})
.catch((err) => {
if (err.name !== 'AbortError') {
setError(err.message);
setLoading(false);
}
});
return () => abortController.abort();
}, [url]); // 依赖url,url变化时重新请求
return { data, loading, error };
}
// 使用示例
function UserList() {
const { data, loading, error } = useFetch('https://api.example.com/users');
if (loading) return <div>加载中...</div>;
if (error) return <div>错误:{error}</div>;
return <ul>{data?.map(user => <li key={user.id}>{user.name}</li>)}</ul>;
}
示例2:窗口尺寸Hook(useWindowSize
)
监听浏览器窗口大小变化。
jsx
import { useState, useEffect } from 'react';
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);
}, []); // 空依赖数组确保effect只运行一次
return size;
}
// 使用示例
function ResponsiveComponent() {
const { width, height } = useWindowSize();
return <div>窗口尺寸:{width}px × {height}px</div>;
}
示例3:表单输入Hook(useInput
)
简化表单控件的状态管理。
ini
import { useState } from 'react';
function useInput(initialValue) {
const [value, setValue] = useState(initialValue);
const handleChange = (e) => setValue(e.target.value);
const reset = () => setValue(initialValue);
return { value, onChange: handleChange, reset };
}
// 使用示例
function LoginForm() {
const username = useInput('');
const password = useInput('');
const handleSubmit = (e) => {
e.preventDefault();
console.log('用户名:', username.value, '密码:', password.value);
username.reset();
password.reset();
};
return (
<form onSubmit={handleSubmit}>
<input type="text" {...username} placeholder="用户名" />
<input type="password" {...password} placeholder="密码" />
<button type="submit">登录</button>
</form>
);
}
示例4:获取上一次的值(usePrevious
)
跟踪状态的前一次值。
javascript
import { useRef, useEffect } from 'react';
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value; // 在渲染后更新ref
}, [value]);
return ref.current; // 返回之前的值
}
// 使用示例
function Counter() {
const [count, setCount] = useState(0);
const prevCount = usePrevious(count);
return (
<div>
当前值: {count}, 上一次值: {prevCount}
<button onClick={() => setCount(c => c + 1)}>增加</button>
</div>
);
}
最佳实践
- 单一职责:每个Hook专注于一个特定功能。
- 优化性能:在effect中清理副作用(如取消请求、移除事件监听)。
- 类型安全:使用TypeScript定义返回类型,提升代码可靠性。
- 测试 :利用
@testing-library/react-hooks
测试Hook行为。
自定义Hooks显著提升了代码的模块化和可维护性,是构建高效React应用的关键工具。