欢迎来到神奇的 React 世界
大家好!在 React 的世界中,有一个强大的秘密武器,它往往隐藏在显而易见的地方,由于缺乏理解或熟悉而没有得到充分利用。
这个强大的工具,被称为自定义 React hooks,可以彻底改变我们编写 React 应用程序代码的方式。通过提取组件中的有状态逻辑,自定义 hooks 允许我们大大提高代码的效率、可读性和重用性。
然而,自定义 hooks 不仅仅是一个开发人员工具箱中的功能或工具。它们代表了我们开发范式的转变,一个引领我们重新思考在 React 应用程序中传统管理状态和副作用方式的新思维模式。
开箱即用:什么是 Hooks?
在本文中,我们将消除自定义 hooks 的概念的迷雾,深入探究它们的结构,并探索创建我们自己的过程。我们将采取实际的方法,举例来演示它们在真实场景中的用法。
我们的目标不仅是理解自定义 hooks 是什么或它们如何工作。这是全面接受其背后的哲学并赏识它们为我们的代码带来的优雅、简单和强大。
为什么自定义 Hooks 很重要
关键在于:hooks 很酷,但是自定义 hooks?它们就是可以使你的 React 代码水平更进一步的秘密配方。就像一个功能强大的工具,可以完全定制你的特定需求。一个为你量身定做的解决方案。
准备潜入自定义 React Hooks 的世界吧!
为什么我们需要自定义 Hooks
你可能会想:"我们已经有 hooks 了,为什么还需要自定义 hooks?"这是一个非常好的问题,答案很简单。自定义 hooks 是关于使你的代码更可读、更易管理和最重要的可重用。
提高代码可读性
在软件开发中,可读性非常重要。编写代码不仅要让它工作,还要让它易于理解。使用自定义 hooks,您可以将复杂的逻辑封装到单独的函数中。这样,当其他人(甚至将来的你)查看代码时,他们可以轻松理解发生了什么。
流线型代码管理
在处理大型应用程序时,管理代码可能是一个具有挑战性的任务。通过使用自定义 hooks,您可以将代码拆分为更小、更易管理的部分。就像整洁地将代码组织到不同的盒子中,每个盒子都服务于特定的目的。
促进代码重用
编程的基本原则之一是 DRY,即 Don’t Repeat Yourself。自定义 hooks 允许您编写一段逻辑一次,并在多个组件中重用它。这意味着更少的重复,更少的错误机会和更高的效率。
简而言之,自定义 hooks 是你的 React 工具箱中的宝贵工具。它们可以帮助您编写更干净,更易维护的代码,使您作为开发人员的生活更轻松,更愉快。
💡 注意: 使用一个开源的工具链比如 Bit 来发布、版本化和在所有的项目中重用你的自定义 hooks ,使用一个简单的
bit import your.username/yourCustomHook
命令,减少代码重复并最小化模板。
理解自定义 Hook
既然我们已经知道为什么需要自定义 hooks,那么让我们深入研究它们的实际样子和功能。 这一点很重要,因为理解自定义 hook 的结构将帮助您在未来编写自己的自定义 hook。
基本外观
自定义 hook 只是一个 JavaScript 函数。唯一的规则是它的名称应该以 “use” 开头。这是一个约定,可以帮助你和其他人识别这个函数是一个 hook。例如,一个自定义钩子可能被命名为 useFetchData
。
自定义 Hook 内部是什么
在自定义 hook 内部,你可以使用其他 hooks,比如 useState
、useEffect
等。例如,useFetchData
钩子可以使用 useState
和 useEffect
钩子来获取数据并在状态中维护它。
返回值
自定义钩子可以返回任何你想要它返回的内容。这可以是一个单一的值、一个数组或者甚至是一个对象。你返回什么取决于你的组件需要什么。在我们的 useFetchData
示例中,自定义钩子可能会返回获取的数据以及加载状态和错误状态。
自定义 Hook 示例
下面是一个简单的例子来说明自定义 hook, 这里有一个自定义钩子的示例来说明其结构:
function useFetchData(url) {const [data, setData] = useState(null); const [loading, setLoading] = useState(true);const [error, setError] = useState(null);useEffect(() => {fetch(url).then(response => response.json()).then(data => {setData(data);setLoading(false); }).catch(error => {setError(error);setLoading(false);});}, [url]);return { data, loading, error };
}
在这个例子中,useFetchData
是一个自定义钩子,它从提供的URL获取数据。它使用useState
来管理数据、加载状态和错误状态,并使用useEffect
来处理数据获取。然后这个钩子返回一个包含数据、加载状态和错误状态的对象。
所以,这就是自定义钩子的结构。一开始它们可能看起来有点复杂,但是一旦你理解了它们的结构,它们可以极大地简化你的 React 代码并使其更易于管理。
创建自己的自定义 Hook 的步骤
Jefferson Santos
既然我们已经解剖了自定义钩子的结构,是时候将这些知识付诸行动,自己创建一个了。别担心,这听起来没有那么可怕。事实上,有了这些简单的步骤,你很快就可以创建自己的自定义钩子了!
第一步:定义你的函数
首先,我们需要定义一个 JavaScript 函数。还记得我们之前讨论过的命名约定吗?函数名称应以 “use” 开头。例如,让我们创建一个处理表单输入的钩子。我们将其命名为 useFormInput
。
function useFormInput() {// 逻辑将在这里
}
第二步:用 Hooks 添加逻辑
接下来,我们将使用 React 中的内置钩子向自定义钩子添加逻辑。 在我们的 useFormInput
钩子中,我们将使用 useState
钩子来管理输入值。
function useFormInput() {const [value, setValue] = useState("");// 更多逻辑将在这里
}
第三步:处理改变
现在,我们需要处理对输入值的更改。 我们将创建一个在输入更改时更新值的函数。
function useFormInput() {const [value, setValue] = useState("");function handleChange(event) {setValue(event.target.value);}// 更多逻辑将在这里
}
第四步:返回需要的
最后,我们需要返回组件需要的任何内容。在这种情况下,我们的组件将需要该值和 handleChange 函数。 所以,我们将返回一个包括两者的对象。
function useFormInput() {const [value, setValue] = useState("");function handleChange(event) {setValue(event.target.value);}return { value, handleChange };
}
就是这样!你刚刚创建了自己的自定义钩子。你现在可以在函数组件中使用这个钩子来处理表单输入。它是可重用的、干净的,并且使你的组件更具可读性。 最棒的是?你可以为任何你想要的创建一个自定义钩子,使你的代码更高效,更易于管理。
10 个实用示例:创建和使用自定义 Hooks
Christina Morillo
既然你已经适应了创建自定义钩子,那么让我们更进一步。 我们将探索十个可立即在项目中使用的自定义钩子的实用示例。 让我们深入研究!
useLocalStorage
useLocalStorage
是一个自定义的钩子,它简化了在 React 应用中使用本地存储的方法。它允许您从本地存储读取、写入和删除数据。
function useLocalStorage(key, initialValue) {const [storedValue, setStoredValue] = useState(() => {try {const item = window.localStorage.getItem(key);return item ? JSON.parse(item) : initialValue;} catch (error) {console.log(error);return initialValue;}});const setValue = value => {try {const valueToStore = value instanceof Function ? value(storedValue) : value;setStoredValue(valueToStore);window.localStorage.setItem(key, JSON.stringify(valueToStore));} catch (error) {console.log(error);}};return [storedValue, setValue];
}
useDocumentTitle
useDocumentTitle
是一个自定义钩子,可以轻松地从 React 组件更改文档标题。
function useDocumentTitle(title) {useEffect(() => {document.title = title;}, [title]);
}
useEventListener
useEventListener
是一个自定义钩子,可以轻松地向任何 DOM 元素添加事件侦听器。
function useEventListener(eventName, handler, element = window) {useEffect(() => {const isSupported = element && element.addEventListener;if (!isSupported) return;element.addEventListener(eventName, handler);return () => {element.removeEventListener(eventName, handler);};}, [eventName, element, handler]);
}
useOnClickOutside
useOnClickOutside
是一个自定义钩子,当用户点击特定组件之外时触发事件。这对于关闭下拉菜单等很方便。
function useOnClickOutside(ref, handler) {useEffect(() => {const listener = event => {if (!ref.current || ref.current.contains(event.target)) return;handler(event);};document.addEventListener("mousedown", listener);document.addEventListener("touchstart", listener);return () => {document.removeEventListener("mousedown", listener);document.removeEventListener("touchstart", listener);};}, [ref, handler]);
}
useHover
useHover
是一个自定义钩子,用于检测鼠标是否悬停在元素上。
function useHover() {const [value, setValue] = useState(false);const ref = useRef(null);const handleMouseOver = () => setValue(true);const handleMouseOut = () => setValue(false);useEffect(() => {const node = ref.current;if (node) {node.addEventListener('mouseover', handleMouseOver);node.addEventListener('mouseout', handleMouseOut);return () => {node.removeEventListener('mouseover', handleMouseOver);node.removeEventListener('mouseout', handleMouseOut);};}},[ref] // Recall only if ref changes);return [ref, value];
}
useOnlineStatus
useOnlineStatus
是一个自定义钩子,用于检测用户当前是否在线或离线。
function useOnlineStatus() {const [isOnline, setOnline] = useState(navigator.onLine);const goOnline = () => setOnline(true);const goOffline = () => setOnline(false);useEffect(() => {window.addEventListener('online', goOnline);window.addEventListener('offline', goOffline);return () => {window.removeEventListener('online', goOnline);window.removeEventListener('offline', goOffline);};}, []);return isOnline;
}
useWindowSize
useWindowSize
是一个自定义钩子,允许你访问当前窗口大小。
function useWindowSize() {const [size, setSize] = useState([window.innerWidth, window.innerHeight]);useLayoutEffect(() => {const updateSize = () => {setSize([window.innerWidth, window.innerHeight]);};window.addEventListener('resize', updateSize);updateSize();return () => window.removeEventListener('resize', updateSize);}, []);return size;
}
useMediaQuery
useMediaQuery
是一个自定义钩子,允许你在组件中使用媒体查询。
function useMediaQuery(query) {const [matches, setMatches] = useState(window.matchMedia(query).matches);useEffect(() => {const mediaQueryList = window.matchMedia(query);const documentChangeHandler = () => setMatches(mediaQueryList.matches);mediaQueryList.addListener(documentChangeHandler);documentChangeHandler();return () => {mediaQueryList.removeListener(documentChangeHandler);};}, [query]);return matches;
}
useDebounce
useDebounce
是一个自定义钩子,有助于延迟函数调用和消除给定的值。
function useDebounce(value, delay) {const [debouncedValue, setDebouncedValue] = useState(value);useEffect(() => {const handler = setTimeout(() => {setDebouncedValue(value);}, delay);return () => {clearTimeout(handler);};}, [value, delay]);return debouncedValue;
}
useInterval
useInterval
是一个自定义钩子,允许你使用指定的间隔设置递归函数调用。这在需要重复执行函数时很有用,比如更新计时器或轮询服务器。
function useInterval(callback, delay) {const savedCallback = useRef();// Remember the latest callback.useEffect(() => {savedCallback.current = callback;}, [callback]);// Set up the interval.useEffect(() => {function tick() {savedCallback.current();}if (delay !== null) {let id = setInterval(tick, delay);return () => clearInterval(id);}}, [delay]);
}
我将暂停到这里,但我相信这些示例应该足以让你了解自定义钩子的可能性。
你可以为你需要的任何创建一个自定义钩子。
所以继续吧,开始创建和使用自定义钩子,使你的代码比以往任何时候都更干净、可重用!