通过自定义 Hook,可以将组件逻辑提取到可重用的函数中。

在我们学习使用 Effect Hook 时,我们已经见过这个聊天程序中的组件,该组件用于显示好友的在线状态:

import React, { useState, useEffect } from 'react';function FriendStatus(props) {const [isOnline, setIsOnline] = useState(null);useEffect(() => {function handleStatusChange(status) {setIsOnline(status.isOnline);}ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);return () => {ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);};});if (isOnline === null) {return 'Loading...';}return isOnline ? 'Online' : 'Offline';
}

现在我们假设聊天应用中有一个联系人列表,当用户在线时需要把名字设置为绿色。我们可以把上面类似的逻辑复制并粘贴到 FriendListItem 组件中来,但这并不是理想的解决方案:

import React, { useState, useEffect } from 'react';function FriendListItem(props) {const [isOnline, setIsOnline] = useState(null);useEffect(() => {function handleStatusChange(status) {setIsOnline(status.isOnline);}ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);return () => {ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);};});return (<li style={{ color: isOnline ? 'green' : 'black' }}>{props.friend.name}</li>);
}

相反,我们希望在 FriendStatusFriendListItem 之间共享逻辑。

目前为止,在 React 中有两种流行的方式来共享组件之间的状态逻辑: render props 和高阶组件,现在让我们来看看 Hook 是如何在让你不增加组件的情况下解决相同问题的。


提取自定义 Hook

当我们想在两个函数之间共享逻辑时,我们会把它提取到第三个函数中。而组件和 Hook 都是函数,所以也同样适用这种方式。

自定义 Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook。 例如,下面的 useFriendStatus 是我们第一个自定义的 Hook:

import { useState, useEffect } from 'react';function useFriendStatus(friendID) {const [isOnline, setIsOnline] = useState(null);useEffect(() => {function handleStatusChange(status) {setIsOnline(status.isOnline);}ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);return () => {ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);};});return isOnline;
}

此处并未包含任何新的内容——逻辑是从上述组件拷贝来的。与组件中一致,请确保只在自定义 Hook 的顶层无条件地调用其他 Hook。

与 React 组件不同的是,自定义 Hook 不需要具有特殊的标识。我们可以自由的决定它的参数是什么,以及它应该返回什么(如果需要的话)。换句话说,它就像一个正常的函数。但是它的名字应该始终以 use 开头,这样可以一眼看出其符合 Hook 的规则。

此处 useFriendStatus 的 Hook 目的是订阅某个好友的在线状态。这就是我们需要将 friendID 作为参数,并返回这位好友的在线状态的原因。

function useFriendStatus(friendID) {const [isOnline, setIsOnline] = useState(null);// ...return isOnline;
}

现在让我们看看应该如何使用自定义 Hook。


使用自定义 Hook

我们一开始的目标是在 FriendStatusFriendListItem 组件中去除重复的逻辑,即:这两个组件都想知道好友是否在线。

现在我们已经把这个逻辑提取到 useFriendStatus 的自定义 Hook 中,然后就可以使用它了:

function FriendStatus(props) {const isOnline = useFriendStatus(props.friend.id);if (isOnline === null) {return 'Loading...';}return isOnline ? 'Online' : 'Offline';
}
function FriendListItem(props) {const isOnline = useFriendStatus(props.friend.id);return (<li style={{ color: isOnline ? 'green' : 'black' }}>{props.friend.name}</li>);
}
  • 这段代码等价于原来的示例代码吗? 等价,它的工作方式完全一样。如果你仔细观察,你会发现我们没有对其行为做任何的改变,我们只是将两个函数之间一些共同的代码提取到单独的函数中。自定义 Hook 是一种自然遵循 Hook 设计的约定,而并不是 React 的特性。

  • 自定义 Hook 必须以 “use” 开头吗?必须如此。这个约定非常重要。不遵循的话,由于无法判断某个函数是否包含对其内部 Hook 的调用,React 将无法自动检查你的 Hook 是否违反了 Hook 的规则。

  • 在两个组件中使用相同的 Hook 会共享 state 吗?不会。自定义 Hook 是一种重用状态逻辑的机制(例如设置为订阅并存储当前值),所以每次使用自定义 Hook 时,其中的所有 state 和副作用都是完全隔离的。

  • 自定义 Hook 如何获取独立的 state?每次调用 Hook,它都会获取独立的 state。由于我们直接调用了 useFriendStatus,从 React 的角度来看,我们的组件只是调用了 useStateuseEffect。 正如我们在之前章节中了解到的一样,我们可以在一个组件中多次调用 useStateuseEffect,它们是完全独立的。

提示:在多个 Hook 之间传递信息

由于 Hook 本身就是函数,因此我们可以在它们之间传递信息。

我们将使用聊天程序中的另一个组件来说明这一点。这是一个聊天消息接收者的选择器,它会显示当前选定的好友是否在线:

const friendList = [{ id: 1, name: 'Phoebe' },{ id: 2, name: 'Rachel' },{ id: 3, name: 'Ross' },
];function ChatRecipientPicker() {const [recipientID, setRecipientID] = useState(1);const isRecipientOnline = useFriendStatus(recipientID);return (<><Circle color={isRecipientOnline ? 'green' : 'red'} /><selectvalue={recipientID}onChange={e => setRecipientID(Number(e.target.value))}>{friendList.map(friend => (<option key={friend.id} value={friend.id}>{friend.name}</option>))}</select></>);
}

我们将当前选择的好友 ID 保存在 recipientID 状态变量中,并在用户从 <select> 中选择其他好友时更新这个 state

由于 useState 为我们提供了 recipientID 状态变量的最新值,因此我们可以将它作为参数传递给自定义的 useFriendStatus Hook:

  const [recipientID, setRecipientID] = useState(1);const isRecipientOnline = useFriendStatus(recipientID);

如此可以让我们知道当前选中的好友是否在线。当我们选择不同的好友并更新 recipientID 状态变量时,useFriendStatus Hook 将会取消订阅之前选中的好友,并订阅新选中的好友状态。


useYourImagination()

自定义 Hook 解决了以前在 React 组件中无法灵活共享逻辑的问题。你可以创建涵盖各种场景的自定义 Hook,如表单处理、动画、订阅声明、计时器,甚至可能还有其他我们没想到的场景。更重要的是,创建自定义 Hook 就像使用 React 内置的功能一样简单。

尽量避免过早地增加抽象逻辑。既然函数组件能够做的更多,那么代码库中函数组件的代码行数可能会剧增。这属于正常现象 —— 不必立即将它们拆分为 Hook。但我们仍鼓励你能通过自定义 Hook 寻找可能,以达到简化代码逻辑,解决组件杂乱无章的目的。

例如,有个复杂的组件,其中包含了大量以特殊的方式来管理的内部状态。useState 并不会使得集中更新逻辑变得容易,因此你可能更愿意使用 redux 中的 reducer 来编写。

function todosReducer(state, action) {switch (action.type) {case 'add':return [...state, {text: action.text,completed: false}];// ... other actions ...default:return state;}
}

Reducers 非常便于单独测试,且易于扩展,以表达复杂的更新逻辑。如有必要,您可以将它们分成更小的 reducer。但是,你可能还享受着 React 内部 state 带来的好处,或者可能根本不想安装其他库。

那么,为什么我们不编写一个 useReducer 的 Hook,使用 reducer 的方式来管理组件的内部 state 呢?其简化版本可能如下所示

function useReducer(reducer, initialState) {const [state, setState] = useState(initialState);function dispatch(action) {const nextState = reducer(state, action);setState(nextState);}return [state, dispatch];
}

在组件中使用它,让 reducer 驱动它管理 state

function Todos() {const [todos, dispatch] = useReducer(todosReducer, []);function handleAddClick(text) {dispatch({ type: 'add', text });}// ...
}

在复杂组件中使用 reducer 管理内部 state 的需求很常见,我们已经将 useReducer 的 Hook 内置到 React 中。

React之Hook(六)——自定义 Hook相关推荐

  1. React:创建用于获取数据的自定义Hook

    Hook 是 React 16.8 的新增特性.它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性. 通过自定义 Hook,可以将组件逻辑提取到可重用的函数中.自定 ...

  2. react 自定义hook

    什么是自定义hook Hook 是 React 16.8 的新增特性.它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性. 通过自定义 Hook,可以将组件逻辑提取 ...

  3. Hook 规则以及自定义Hook

    文章目录 Hook规则 只在最顶层使用 Hook 只在 React 函数中调用 Hook 自定义Hook Hook规则 只在最顶层使用 Hook 不要在循环,条件或嵌套函数中调用 Hook, 确保总是 ...

  4. hook xposed 自定义类_【开始学习React Hook(1)】Hook之useState

    react hook是react推出的一种特殊函数.这些函数可以让你在不创建react class的情况下依然可以使用react的一些特性(诸如目前react的钩子函数拥有的所有特性). 最常用的ho ...

  5. React Hooks的使用(三)——useRef、useImperativeHandle、useLayoutEffect解析、自定义Hook

    一.useRef useRef返回一个ref对象,返回的ref对象再组件的整个生命周期保持不变. 最常用的ref是两种用法: 用法一:引入DOM(或者组件,但是需要是class组件)元素: 案例一:引 ...

  6. React 全家桶(react脚手架 redux react-redux react-router-dom ui库 reactHook)含 自定义hook的方法及使用

    文章目录 React 入门 React 简介 React 为何物 为何学习 React React 初体验 来一发 Hello React 创建虚拟 DOM 的两种方式:JS 和 JSX 虚拟 DOM ...

  7. 你应该会喜欢的5个自定义 Hook

    作者:Grégory D'Angelo 译者:前端小智 来源: dev 点赞再看,微信搜索**[大迁世界],B站关注[前端小智]**这个没有大厂背景,但有着一股向上积极心态人.本文 GitHub ht ...

  8. hook——useState——useState的并发写法——受控组件—— 自定义hook函数-封装hook

    简介 在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数组件,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们 ...

  9. hooks 系列七:自定义 hook

    快来加入我们吧! "小和山的菜鸟们",为前端开发者提供技术相关资讯以及系列基础文章.为更好的用户体验,请您移至我们官网小和山的菜鸟们 ( https://xhs-rookies.c ...

最新文章

  1. Oracle Execute Plan原理分析与实例分享之一
  2. [学习方法]如何解析源代码
  3. 怎么说呢。留个纪念,关于字符串的重载
  4. python后端开发学什么_零基础学Python,这是阿里Python8年开发经验写给你的学习路线图...
  5. Qt工作笔记-WebEngineView调用web站点中的JS脚本(含Vue Cli脚本)
  6. pdf裁边app_哪款手机PDF阅读APP值得推荐?
  7. html 怎么把表格不给输入,如何让用户在HTML5网页表单中输入持续时间而不会让他们烦恼?...
  8. 阿里架构师手写Tomcat——Session源码解析
  9. 自己动手写ORM框架
  10. zendstudio的安装与配置
  11. Linux 抄袭 Unix ?今日终有定论!
  12. phpstudy mysql配置_phpstudy mysql数据库文件位置在哪
  13. android 蓝牙通讯测试工具,Android Bluetooth 学习(2)应用层实现蓝牙设备查找、tcp_ip通信...
  14. JAVA-map转换json异常
  15. 年轻人的第一笔债,在双11的直播间里
  16. 当AI邂逅电能:与图知科技一起探索工业AI王座|白洞战报
  17. 【错误解决】Ubuntu20.04安装输入法遇到的问题
  18. 3dMax 制作玻璃材质
  19. PowerDesigner菜单翻译
  20. 利用Sympy计算sin1°的最小多项式

热门文章

  1. Source Insight 4.0设置注释与反注释的快捷键
  2. 空闲时间可以做些什么赚钱、推荐几个业余时间做的兼职副业
  3. 南京工业大学2023数据结构复习题简析【编程题2】
  4. 博观而约取 温故以知新——“21世纪的计算大会”报道
  5. 艾恩ASP无组件上传最新更新说明
  6. sklearn高级功能
  7. 没有core的程序异常退出追查过程
  8. 蓝桥杯第十一届单片机国赛
  9. [免费专栏] Android安全之Android APP应用程序的汉化功能 (修改so中的字符串内容)
  10. SIGIR 2022 | FRNet:上下文感知的特征强化模块