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 - Hooks 进行状态管理 ReactJS - Hooks 的组件生命周期 ReactJS - 布局组件 ReactJS - 分页 ReactJS - Material UI ReactJS - Http 客户端编程 ReactJS - 表单编程 ReactJS - 受控组件 ReactJS - 非受控组件 ReactJS - Formik ReactJS - 条件渲染 ReactJS - 列表 ReactJS - Key 键 ReactJS - 路由 ReactJS - Redux ReactJS - 动画 ReactJS - Bootstrap ReactJS - Map ReactJS - 表格 ReactJS - 使用 Flux 管理状态 ReactJS - 测试 ReactJS - CLI 命令 ReactJS - 构建和部署 ReactJS - 示例

Hooks

ReactJS - Hooks 简介 ReactJS - 使用 useState ReactJS - 使用 useEffect ReactJS - 使用 useContext ReactJS - 使用 useRef ReactJS - 使用 useReducer ReactJS - 使用 useCallback ReactJS - 使用 useMemo ReactJS - 自定义 Hooks

ReactJS 高级

ReactJS - 可访问性 ReactJS - 代码拆分 ReactJS - 上下文 ReactJS - 错误边界 ReactJS - 转发 Refs ReactJS - 片段 ReactJS - 高阶组件 ReactJS - 与其他库集成 ReactJS - 优化性能 ReactJS - Profiler API ReactJS - Portals ReactJS - 不使用 ES6 ECMAScript ReactJS - 不使用 JSX 的 React ReactJS - Reconciliation ReactJS - Refs 和 DOM ReactJS - 渲染道具 ReactJS - 静态类型检查 ReactJS - 严格模式 ReactJS - Web 组件

其他概念

ReactJS - 日期选择器 ReactJS - Helmet ReactJS - 内联样式 ReactJS - PropTypes ReactJS - BrowserRouter ReactJS - DOM ReactJS - 轮播 ReactJS - 图标 ReactJS - 表单组件 ReactJS - 参考 API

ReactJS 有用资源

ReactJS - 快速指南 ReactJS - 备忘录 Axios - 备忘录 ReactJS - 有用资源 ReactJS - 讨论


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)

这里,bottomsetBottomuseInfiniteScroll 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

最后,打开浏览器并检查输出。一旦用户到达页面末尾,应用程序就会附加新的待办事项,并无限继续,如下所示 −

Implementing UseInfiniteScroll Hook

行为与 TodoList 组件相同。我们已成功从组件中提取逻辑并使用它来创建我们的第一个自定义Hooks(钩子)。现在,自定义Hooks(钩子)可用于任何应用程序。

摘要

自定义Hooks(钩子)是一种现有功能,用于将可重用逻辑与功能组件分开,并允许使其成为跨不同功能组件真正可重用的Hooks(钩子)。