如何在 React 中创建自定义 Hooks
什么是自定义挂钩?
自定义钩子允许你将一些组件逻辑提取到一个可重用的函数中。
自定义钩子是一个 JavaScript 函数,它以`use`开头,并且可以调用其他钩子。请记住,组件和钩子都是函数,所以我们实际上并没有创造任何新概念。我们只是将代码重构为另一个函数,使其可重用。
如果没有自定义钩子
假设我们的组件中有一个用于获取窗口宽度的功能。我们想知道用户何时调整屏幕大小。例如,为了修改设计,我们需要知道屏幕何时变小。我们可以这样写:
const LayoutComponent = () => {
const [onSmallScreen, setOnSmallScreen] = useState(false);
useEffect(() => {
checkScreenSize();
window.addEventListener("resize", checkScreenSize);
}, []);
let checkScreenSize = () => {
setOnSmallScreen(window.innerWidth < 768);
};
return (
<div className={`${onSmallScreen ? "small" : "large"}`}>
<h1>Hello World!</h1>
</div>
);
};
这里有一个组件,它有一个onSmallScreen状态,用于判断当前窗口的宽度是否小于 768 像素。为了实现这一点,我们使用了一个useEffect hook。在这个 hook 内部,我们首先调用checkScreenSize函数来更新onSmallScreen状态变量。最后,我们将checkScreenSize函数绑定到一个 resize 事件监听器,以便在 resize 事件发生时,根据需要更新该状态。
创建自定义钩子
这样做完全没问题。当窗口宽度小于 600 像素时,类名就变成small;当宽度大于 600 像素时,类名就变回large。
现在,假设我想在应用程序的其他地方使用控制窗口宽度的功能。我应该直接复制粘贴这段代码吗?当然可以,但我们可以将此功能提取到一个自定义钩子中,并在任何需要的地方重复使用。
因为 hooks 只是 Javascript 函数,所以它们实际上不需要 React 组件就能存在。
我将创建一个名为useWindowWidth.js的新文件:
import { useState, useEffect } from "react";
const useWindowsWidth = () => {
const [isScreenSmall, setIsScreenSmall] = useState(false);
let checkScreenSize = () => {
setIsScreenSmall(window.innerWidth < 600);
};
useEffect(() => {
checkScreenSize();
window.addEventListener("resize", checkScreenSize);
return () => window.removeEventListener("resize", checkScreenSize);
}, []);
return isScreenSmall;
};
export default useWindowsWidth;
我们将此功能提取到了useWindowWidth函数中。现在,我们可以在任何需要使用它的地方导入它!
import React from 'react'
import useWindowWidth from './useWindowWidth.js'
const MyComponent = () => {
const onSmallScreen = useWindowWidth();
return (
// Return some elements
)
}
是不是很棒?我之前的一个项目里也用到过类似的功能。我需要知道窗口的大小,以便调整渲染的元素。使用自定义钩子减少了重复代码的数量。
当然,任何在组件的 hook 中使用到的内容都可以提取出来,并在自定义 hook 中使用。
例如,假设你有一些组件,用于显示基于文章的评论列表。我们可以设想类似这样的情况:
const ArticleWithComments = (articleId) => {
const [comments, setComments] = useState([])
const [error, setError] = useState(null)
let handleCommentsSuccessFetch = (articleComments) => setComments(articleComments)
let handleError = error => setError(error)
useEffect(() => {
fetchComments(articleId, handleCommentsSuccessFetch, handleError)
}, [])
return (
// Do something in the DOM
)
}
const BlogPostWithComments = (blogPostId) => {
const [comments, setComments] = useState([])
const [error, setError] = useState(null)
let handleCommentsSuccessFetch = (blogPostComments) => setComments(blogPostComments)
let handleError = error => setError(error)
useEffect(() => {
fetchComments(blogPostId, handleCommentsSuccessFetch, handleError)
}, [])
return (
// Do something in the DOM
)
}
在这个例子中,我们有两个组件。它们都根据 ID 获取评论列表,ID 可以是文章 ID,也可以是博客文章 ID。在useEffect hook 中,我们调用了一个 API 来获取这些评论,并执行两个函数。第一个函数在成功时将评论设置到状态中,第二个函数在出错时将错误信息设置到状态中。
但是,这两个组件的功能存在重复。幸运的是,我们可以将此功能提取到自定义钩子中:
const useCommentsRetriever = (entityId) => {
const [comments, setComments] = useState([]);
const [error, setError] = useState(null);
let handleCommentsSuccessFetch = (comments) => setComments(comments);
let handleError = (error) => setError(error);
useEffect(() => {
fetchComments(entityId, handleCommentsSuccessFetch, handleError);
}, []);
return [comments, error];
};
这里,我们引入了`useCommentsRetriever`这个钩子函数。它接受一个`entityId`作为参数,也就是我们文章或博客文章的 ID。它和组件中的钩子函数类似,区别在于这个自定义钩子函数需要返回一些内容。我选择返回一个数组,数组的第一个元素是评论,第二个元素是错误信息。
它的用法如下:
//Import the custom hook
import useCommentsRetriever from './useCommentsRetriever.js'
const ArticleWithComments = (articleId) => {
const [comments, error] = useCommentsRetriever(articleId)
return (
// Do something in the DOM
)
}
const BlogPostWithComments = (blogPostId) => {
const [comments, error] = useCommentsRetriever(blogPostId)
return (
// Do something in the DOM
)
}
看看我们少写了多少代码?`useCommentsRetriever`函数接受一个 id 作为参数。这[comments, error]就是我们所说的数组解构。`useCommentsRetriever`函数返回一个数组。我们将该数组的第一个元素赋值给名为 `comments` 的变量,将第二个元素赋值给名为 `error` 的变量。
请注意,我可以随意命名这些变量。我也可以在两个组件中使用不同的名称。因此,当您在useState hook 中看到相同的语法时,这是因为useState hook 也返回一个数组 😉
我有一篇关于状态钩子的文章,如果你想了解更多,可以看看。
我们必须以“使用”开头来创建自定义钩子吗?
根据 React 文档,是的。
您可以查看文档中的钩子规则以获取更多信息。
隔离自定义钩子
如果在两个组件中使用相同的自定义 Hook,它们将不会共享状态。例如,BlogPostWithComments 组件的状态将与 ArticleWithComments 组件的状态完全分离。每个自定义 Hook 都会创建一个新函数,该函数会使用React 的useState和useEffect。我们可以在同一个组件中使用多个 Hook,逻辑相同。
天空才是极限
自定义 Hooks 让你在编写 React 代码时真正发挥想象力。你可以提取并共享逻辑,这是类组件无法实现的。
玩得开心❤️
文章来源:https://dev.to/damcosset/how-to-create-custom-hooks-in-react-44nd
