简介
React 16.8 版本引入了 Hooks,使得函数组件能够使用状态和其他React特性,React 中内置了一些 Hook,比如 useState,useContext 和 useEffect。有时我们需要一个用途更特殊的 Hook,这时我们就可以根据应用需求创建自己的 Hook。
优势
- 可以聚合功能代码使代码可读性更强,有利于维护
- 可以在多个组件之间重用状态逻辑,提高代码的可重用性
限制
- 没有使用hook的函数不要加
use - 使用了hook的自定义hook名称必须以
use开头,然后紧跟一个大写字母
实现一个自定义hook
借助官方的示例实现一个网络状态同步hook,常规写法如下:
import React, { useEffect, useState } from 'react';
function App() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
const handleOnline = () => setIsOnline(true);
const handleOffline = () => setIsOnline(false);
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
// 组件释放取消事件监听
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
},[]);
return (
<div className="App">
<h3>{isOnline ? '✅ 在线' : '❌ 离线'}</h3>
</div>
);
}
export default App;
从上面示例中可以看出我们只关注网络状态,不需要关心网络监听、移除等操作,该功能可以看作是一个独立的功能与当前页面没有关系 ,我们可以将其独立为 useOnlineStatus 的hook,让其与 useState 和 useEffect 相似。
import React, { useEffect, useState } from 'react';
// 自定义hook
function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
const handleOnline = () => setIsOnline(true);
const handleOffline = () => setIsOnline(false);
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
// 组件释放取消事件监听
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
}
}, []);
return isOnline;
}
function App() {
// 使用hook
const isOnline = useOnlineStatus();
return (
<div className="App">
<h3>{isOnline ? '✅ 在线' : '❌ 离线'}</h3>
</div>
);
}
export default App;
实现一个输入hook
自定义 Hook 共享的只是状态逻辑而不是状态本身,每个调用都完全独立于对同一个 Hook 的其他调用。
import React, { useEffect, useState } from 'react';
import './App.css';
function useFormInput(initialValue: string) {
const [value, setValue] = useState(initialValue);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
};
return {
value,
onChange: handleChange,
};
}
function App() {
// 使用hook
const javaScriptProps = useFormInput('JavaScript');
const typeScriptProps = useFormInput('TypeScript');
return (
<div className="App">
<h2>{javaScriptProps.value}-{typeScriptProps.value}</h2>
<input type="text" {...javaScriptProps} />
<input type="text" {...typeScriptProps} />
</div>
);
}
export default App;
实现一个跳过首次触发的Effect
/**
* useEffect跳过首次渲染
* @param effect 回调
* @param dependencies 依赖项,依赖项改变时才执行回调
*/
function useSkipFirstEffect(effect: () => void, dependencies: any[]) {
// 记录回调函数是否是第一次渲染
const isFirstRender = useRef(true);
useEffect(() => {
if (isFirstRender.current) { // 第一次渲染不执行
isFirstRender.current = false;
} else { // 第二次及以后执行回调
effect();
}
}, dependencies)
}
实现一个剪切板hook
import { useCallback, useState } from "react";
const useClipboard = () => {
const [isCopied, setIsCopied] = useState(false);
const copy = useCallback(async (text: string) => {
// 判断是否支持剪贴板
if (!navigator.clipboard) {
// 打印警告
console.warn("当前浏览器不支持剪贴板");
return;
}
// try catch
try {
navigator.clipboard.writeText(text);
setIsCopied(true);
// 延迟1.5秒后清除状态
setTimeout(() => {
setIsCopied(false);
}, 1500);
} catch (error) {
console.warn("复制失败", error);
setIsCopied(false);
}
}, []);
return { isCopied, copy }
}
export default useClipboard;
参考
友情提示
见原文:【React】自定义hook)
本文同步自微信公众号 "程序员小溪" ,这里只是同步,想看及时消息请移步我的公众号,不定时更新我的学习经验。