- ReactJS 教程
- ReactJS - 首页
- ReactJS - 简介
- ReactJS - 路线图
- ReactJS - 安装
- ReactJS - 特性
- ReactJS - 优点与缺点
- ReactJS - 架构
- ReactJS - 创建 React 应用
- ReactJS - JSX
- ReactJS - 组件
- ReactJS - 嵌套组件
- ReactJS - 使用新创建的组件
- ReactJS - 组件集合
- ReactJS - 样式
- ReactJS - 属性 (props)
- ReactJS - 使用属性创建组件
- ReactJS - props 验证
- ReactJS - 构造函数
- ReactJS - 组件生命周期
- ReactJS - 事件管理
- ReactJS - 创建一个事件感知组件
- ReactJS - 在 Expense Manager 应用中引入事件
- ReactJS - 状态管理
- ReactJS - 状态管理 API
- ReactJS - 无状态组件
- ReactJS - 使用 React Hooks 进行状态管理
- ReactJS - 使用 React Hooks 进行组件生命周期管理
- ReactJS - 布局组件
- ReactJS - 分页
- ReactJS - Material UI
- ReactJS - Http 客户端编程
- ReactJS - 表单编程
- ReactJS - 受控组件
- ReactJS - 非受控组件
- ReactJS - Formik
- ReactJS - 条件渲染
- ReactJS - 列表
- ReactJS - Keys
- ReactJS - 路由
- ReactJS - Redux
- ReactJS - 动画
- ReactJS - Bootstrap
- ReactJS - Map
- ReactJS - 表格
- ReactJS - 使用 Flux 管理状态
- ReactJS - 测试
- ReactJS - CLI 命令
- ReactJS - 构建和部署
- ReactJS - 例子
- Hooks
- ReactJS - Hook 简介
- ReactJS - 使用 useState
- ReactJS - 使用 useEffect
- ReactJS - 使用 useContext
- ReactJS - 使用 useRef
- ReactJS - 使用 useReducer
- ReactJS - 使用 useCallback
- ReactJS - 使用 useMemo
- ReactJS - 自定义 Hook
- ReactJS 高级
- ReactJS - 可访问性
- ReactJS - 代码分割
- ReactJS - Context
- ReactJS - 错误边界
- ReactJS - 转发 Refs
- ReactJS - 片段
- ReactJS - 高阶组件
- ReactJS - 与其他库集成
- ReactJS - 性能优化
- ReactJS - Profiler API
- ReactJS - Portals
- ReactJS - 无 ES6 ECMAScript 的 React
- ReactJS - 无 JSX 的 React
- ReactJS - 协调
- ReactJS - Refs 和 DOM
- ReactJS - Render Props
- ReactJS - 静态类型检查
- ReactJS - Strict Mode
- ReactJS - Web Components
- 附加概念
- ReactJS - 日期选择器
- ReactJS - Helmet
- ReactJS - 内联样式
- ReactJS - PropTypes
- ReactJS - BrowserRouter
- ReactJS - DOM
- ReactJS - 走马灯
- ReactJS - 图标
- ReactJS - 表单组件
- ReactJS - 参考 API
- ReactJS 有用资源
- ReactJS - 快速指南
- ReactJS - 有用资源
- ReactJS - 讨论
ReactJS - 自定义 Hook
Hooks 是函数组件不可分割的一部分。它们可以用来增强函数组件的功能。React 提供了一些内置 Hook。尽管内置 Hook 功能强大,可以实现任何功能,但专用 Hook 仍然是必要的,并且需求量很大。
React 了解了开发者的这种需求,并允许通过现有的 Hook 创建新的自定义 Hook。开发者可以从函数组件中提取特殊的功能,并将其创建为一个单独的 Hook,可以在任何函数组件中使用。
让我们在本节学习如何创建自定义 Hook。
创建自定义 Hook
让我们创建一个具有无限滚动功能的新 React 函数组件,然后从函数组件中提取无限滚动功能并创建一个自定义 Hook。创建自定义 Hook 后,我们将尝试更改原始函数组件以使用我们的自定义 Hook。
实现无限滚动功能
组件的基本功能是简单地通过生成虚拟待办事项列表来显示它。当用户滚动时,组件将生成一组新的虚拟待办事项列表并将其附加到现有列表。
首先,创建一个新的 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;
接下来,创建一个计数状态变量来维护要生成的待办事项数量。
const [count, setCount] = useState(100)
这里:
初始要生成的项目数量为 100
count 是用于维护待办事项数量的状态变量
接下来,创建一个数据数组来维护生成的虚拟待办事项,并使用 useMemo Hook 保留引用。
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 Hook 确保 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 Hook
接下来,让我们尝试通过从现有组件中提取逻辑来创建新的自定义 Hook,然后在单独的组件中使用它。创建一个新的自定义 Hook,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关键字命名自定义 Hook。这是使用use关键字作为自定义 Hook 名称开头的惯例,它也是 React 的提示,提示该函数是一个自定义 Hook
使用状态变量 bottom 来了解用户是否到达页面底部。
使用 useEffect 在 DOM 可用后注册滚动事件
使用泛型函数 loadDataFn 在组件中创建 Hook 时提供。这将能够为加载数据创建自定义逻辑。
在滚动事件处理程序中使用 DOM 属性来跟踪用户滚动位置。当用户到达页面底部时,bottom 状态变量会发生变化。
返回 bottom 状态变量的当前值 (bottom) 和用于更新 bottom 状态变量的函数 (setBottom)
接下来,创建一个新组件 TodoListUsingCustomHook 来应用新创建的 Hook。
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([])
接下来,应用无限滚动自定义 Hook。
const [bottom, setBottom] = useInfiniteScroll(loadMoreData)
这里,bottom 和 setBottom 由 useInfiniteScroll Hook 公开。
接下来,创建用于生成待办事项列表的函数 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) }
这里:
根据计数状态变量生成待办事项
将计数状态变量增加 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 函数将生成的待办事项列表设置为 data 状态变量
使用 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 组件相同。我们已成功地从组件中提取逻辑并将其用于创建我们的第一个自定义 Hook。现在,可以在任何应用程序中使用自定义 Hook。
总结
自定义 Hook 是一种现有的功能,用于将可重用的逻辑与函数组件分离,并允许在不同的函数组件中使其成为真正可重用的 Hook。