ReactJS - 自定义Hooks(钩子)
Hooks(钩子)是函数组件不可或缺的一部分。它们可用于增强函数组件的功能。React 提供了一些内置Hooks(钩子)。尽管内置Hooks(钩子)功能强大且可以执行任何功能,但专用Hooks(钩子)是必需的,而且需求量很大。
React 了解开发人员的这种需求,并允许通过现有Hooks(钩子)创建新的自定义Hooks(钩子)。开发人员可以从函数组件中提取特殊功能,并将其创建为单独的Hooks(钩子),可用于任何函数组件。
让我们在本章中学习如何创建自定义Hooks(钩子)。
创建自定义Hooks(钩子)
让我们创建一个具有无限滚动功能的新 React 函数组件,然后从函数组件中提取无限功能并创建自定义Hooks(钩子)。创建自定义Hooks(钩子)后,我们将尝试更改原始函数组件以使用我们的自定义Hooks(钩子)。
实现无限滚动功能
组件的基本功能是通过生成待办事项的虚拟列表来显示它。当用户滚动时,组件将生成一组新的虚拟待办事项列表并将其附加到现有列表中。
首先,创建一个新的 React 应用程序并使用以下命令启动它。
create-react-app myapp cd myapp npm start
接下来,在组件文件夹 (src/components/TodoList.js) 下创建一个 React 组件 TodoList
function TodoList() { return <div>Todo List</div> } export default TodoList
接下来,更新根组件 App.js 以使用新创建的 TodoList 组件。
import TodoList from './components/TodoList'; function App() { return ( <div style={{ padding: "5px"}}> <TodoList /> </div> ); } export default App;
接下来,创建一个 count 状态变量来维护要生成的待办事项数量。
const [count, setCount] = useState(100)
这里,
要生成的初始项目数为 100
count 是用于维护待办事项数量的状态变量
接下来,创建一个数据数组来维护生成的虚拟待办事项并使用 useMemo Hooks(钩子)保留引用。
React.useMemo(() => { for (let i = 1; i <= count; i++) { data.push({ id: i, todo: "Todo Item " + i.toString() }) } }, [count]);
这里,
使用 useMemo 限制每次渲染时生成待办事项。
使用 count 作为依赖项,在计数更改时重新生成待办事项
接下来,将处理程序附加到滚动事件,并更新待办事项的计数,以便在用户移动到页面末尾时生成。
React.useEffect(() => { function handleScroll() { const isBottom = window.innerHeight + document.documentElement.scrollTop === document.documentElement.offsetHeight; if (isBottom) { setCount(count + 100) } } window.addEventListener("scroll", handleScroll); return () => { window.removeEventListener("scroll", handleScroll); }; }, [])
这里我们有,
使用 useEffect Hooks(钩子)来确保 DOM 已准备好附加事件。
为滚动事件附加事件处理程序 scrollHandler。
从应用程序中卸载组件时删除事件处理程序
发现用户使用滚动事件处理程序中的 DOM 属性点击页面底部。
一旦用户点击页面底部,计数就会增加 100
一旦计数状态变量发生变化,组件就会重新渲染,并加载更多待办事项。
最后,渲染待办事项,如下所示−
<div> <p>List of Todo list</p> <ul> {data && data.map((item) => <li key={item.id}>{item.todo}</li> )} </ul> </div>
组件完整源代码如下 −
import React, { useState } from "react" function TodoList() { const [count, setCount] = useState(100) let data = [] React.useMemo(() => { for (let i = 1; i <= count; i++) { data.push({ id: i, todo: "Todo Item " + i.toString() }) } }, [count]); React.useEffect(() => { function handleScroll() { const isBottom = window.innerHeight + document.documentElement.scrollTop === document.documentElement.offsetHeight; if (isBottom) { setCount(count + 100) } } window.addEventListener("scroll", handleScroll); return () => { window.removeEventListener("scroll", handleScroll); }; }, []) return ( <div> <p>List of Todo list</p> <ul> {data && data.map((item) => <li key={item.id}>{item.todo}</li> )} </ul> </div> ) } export default TodoList
接下来,打开浏览器并运行应用程序。一旦用户到达页面末尾,应用程序就会附加新的待办事项,并无限继续,如下所示 −

实现 useInfiniteScroll Hooks(钩子)
接下来,让我们尝试通过从现有组件中提取逻辑来创建新的自定义Hooks(钩子),然后在单独的组件中使用它。创建一个新的自定义Hooks(钩子) useInfiniteScroll (src/Hooks/useInfiniteScroll.js),如下所示
import React from "react"; export default function useInfiniteScroll(loadDataFn) { const [bottom, setBottom] = React.useState(false); React.useEffect(() => { window.addEventListener("scroll", handleScroll); return () => { window.removeEventListener("scroll", handleScroll); }; }, []); React.useEffect(() => { if (!bottom) return; loadDataFn(); }, [bottom]); function handleScroll() { if (window.innerHeight + document.documentElement.scrollTop != document.documentElement.offsetHeight) { return; } setBottom(true) } return [bottom, setBottom]; }
这里我们有,
使用 use 关键字来命名自定义Hooks(钩子)。这是以 use 关键字开始自定义Hooks(钩子)名称的一般做法,也是让反应知道该函数是自定义Hooks(钩子)的提示
使用状态变量 bottom 来了解用户是否点击了页面底部。
使用 useEffect 在 DOM 可用时注册滚动事件
使用通用函数 loadDataFn 在组件中创建Hooks(钩子)期间提供。这将能够创建用于加载数据的自定义逻辑。
在滚动事件处理程序中使用 DOM 属性来跟踪用户滚动位置。当用户点击页面底部时,底部状态变量会发生变化。
返回底部状态变量的当前值(bottom)和更新底部状态变量的函数(setBottom)
接下来,创建一个新组件 TodoListUsingCustomHook 来应用新创建的Hooks(钩子)。
function TodoListUsingCustomHook() { return <div>Todo List</div> } export default TodoListUsingCustomHook
接下来,更新根组件 App.js 以使用新创建的 TodoListUsingCustomHook 组件。
import TodoListUsingCustomHook from './components/TodoListUsingCustomHook'; function App() { return ( <div style={{ padding: "5px"}}> <TodoListUsingCustomHook /> </div> ); } export default App;
接下来,创建一个状态变量 data 来管理待办事项列表项。
const [data, setData] = useState([])
接下来,创建一个状态变量 count 来管理要生成的待办事项列表项的数量。
const [data, setData] = useState([])
接下来,应用无限滚动自定义Hooks(钩子)。
const [bottom, setBottom] = useInfiniteScroll(loadMoreData)
这里,bottom 和 setBottom 由useInfiniteScroll Hooks(钩子)。
接下来,创建函数来生成待办事项列表项,loadMoreData
function loadMoreData() { let data = [] for (let i = 1; i <= count; i++) { data.push({ id: i, todo: "Todo Item " + i.toString() }) } setData(data) setCount(count + 100) setBottom(false) }
这里,
根据 count 状态变量生成待办事项
将 count 状态变量增加 100
使用 setData 函数将生成的待办事项列表设置为 data 状态变量
使用 setBottom 函数将 bottom 状态变量设置为 false
接下来,生成初始待办事项,如下所示 −
const loadData = () => { let data = [] for (let i = 1; i <= count; i++) { data.push({ id: i, todo: "Todo Item " + i.toString() }) } setData(data) } React.useEffect(() => { loadData(count) }, [])
这里,
根据初始计数状态变量生成初始待办事项
使用 setData 函数将生成的待办事项列表设置为数据状态变量
使用 useEffect 确保一旦 DOM 可用就会生成数据
最后,按如下所示渲染待办事项 −
<div> <p>List of Todo list</p> <ul> {data && data.map((item) => <li key={item.id}>{item.todo}</li> )} </ul> </div>
组件完整源代码如下所示 −
import React, { useState } from "react" import useInfiniteScroll from '../Hooks/useInfiniteScroll' function TodoListUsingCustomHook() { const [data, setData] = useState([]) const [count, setCount] = useState(100) const [bottom, setBottom] = useInfiniteScroll(loadMoreData) const loadData = () => { let data = [] for (let i = 1; i <= count; i++) { data.push({ id: i, todo: "Todo Item " + i.toString() }) } setData(data) } function loadMoreData() { let data = [] for (let i = 1; i <= count; i++) { data.push({ id: i, todo: "Todo Item " + i.toString() }) } setData(data) setCount(count + 100) setBottom(false) } React.useEffect(() => { loadData(count) }, []) return ( <div> <p>List of Todo list</p> <ul> {data && data.map((item) => <li key={item.id}>{item.todo}</li> )} </ul> </div> ) } export default TodoListUsingCustomHook
最后,打开浏览器并检查输出。一旦用户到达页面末尾,应用程序就会附加新的待办事项,并无限继续,如下所示 −

行为与 TodoList 组件相同。我们已成功从组件中提取逻辑并使用它来创建我们的第一个自定义Hooks(钩子)。现在,自定义Hooks(钩子)可用于任何应用程序。
摘要
自定义Hooks(钩子)是一种现有功能,用于将可重用逻辑与功能组件分开,并允许使其成为跨不同功能组件真正可重用的Hooks(钩子)。