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)

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

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

Implementing UseInfiniteScroll Hook

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

摘要

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