当前位置:首页 > 科技  > 软件

useEffect 实践案例:自定义 Hook

来源: 责编: 时间:2023-12-21 17:11:29 140观看
导读我们将在上一章案例的基础之上学习自定义 hook。在上一章中,我们巧妙的把大量的 JSX 逻辑处理封装在了 List 组件中,使得在页面组件的代码变得非常简单。这是针对 UI 层的逻辑处理,那么在数据的处理上,是否也能够进行一些

pKf28资讯网——每日最新资讯28at.com

我们将在上一章案例的基础之上学习自定义 hook。pKf28资讯网——每日最新资讯28at.com

在上一章中,我们巧妙的把大量的 JSX 逻辑处理封装在了 List 组件中,使得在页面组件的代码变得非常简单。这是针对 UI 层的逻辑处理,那么在数据的处理上,是否也能够进行一些封装呢?pKf28资讯网——每日最新资讯28at.com

// 数据的主要核心逻辑const str = useRef('')const [list, setList] = useState<string[]>([])const [error, setError] = useState('')const [loading, setLoading] = useState(true)function getList() {  searchApi(str.current).then(res => {    setList(res)    setLoading(false)    setError('')  }).catch(err => {    setLoading(false)    setError(err)  })}useEffect(() => {  loading && getList()}, [loading])function onSure() {  setLoading(true)}

答案是肯定的,解决方案就是我们将要在本章中学习的自定义 hook。pKf28资讯网——每日最新资讯28at.com

一、自定义hook

我们常常会封装一个函数用于逻辑的复用。自定义 hook 也是这样的一个在 react 组件内部用于逻辑复用的函数封装。pKf28资讯网——每日最新资讯28at.com

和普通函数封装相比,他唯一的特殊之处就在于我们常常会将 react 内置 hook 封装在逻辑之中,比如 useState,useEffect 等。除此之外,为了区分与普通的函数封装,我们必须以 use 开头为自定义 hook 命名,这样的 hook 只能在 React 组件中使用。pKf28资讯网——每日最新资讯28at.com

以上一章中的数据处理逻辑为例,我们来封装一个自定义 hook,将其命名为 useFetch。pKf28资讯网——每日最新资讯28at.com

function useFetch() {}

我们先考虑单个场景的封装,单纯只是为了让组件看上去更简洁。pKf28资讯网——每日最新资讯28at.com

我们就可以把所有的数据和处理数据的逻辑封装起来。pKf28资讯网——每日最新资讯28at.com

import {useEffect, useState, useRef} from 'react'import { searchApi } from './api'export default function useFetch() {  const str = useRef('')  const [list, setList] = useState<string[]>([])  const [error, setError] = useState('')  const [loading, setLoading] = useState(true)  function getList() {    searchApi(str.current).then(res => {      setList(res)      setLoading(false)      setError('')    }).catch(err => {      setLoading(false)      setError(err)    })  }  useEffect(() => {    loading && getList()  }, [loading])  return { str, list, error, loading, setLoading }}

封装过程非常简单,就是把之前那一堆逻辑全部迁移过来,最后返回应用组件里需要的数据和方法即可。pKf28资讯网——每日最新资讯28at.com

return { str, list, error, loading, setLoading }

OK,此时我们来观察一下组件里的代码。pKf28资讯网——每日最新资讯28at.com

export default function DemoOneNormal() {  const {loading, setLoading, str, list, error} = useFetch()    return (    <Block className={s.container} title={td.title} desc={td.desc}>      <div className={r.flex}>        <input          className={s.input}          placeholder="请输入您要搜索的内容"          onChange={(e) => str.current = e.target.value}        />        <Button          className={s.button}          onClick={() => setLoading(true)}        >          搜索        </Button>      </div>      <List        list={list}        loading={loading}        error={error}        renderItem={(item) => (          <div key={item} className={s.item}>{item}</div>        )}      />    </Block>  )}

逻辑简洁了许多。变成了简单的同步代码:通过一个方法获取数据,并将数据渲染到 UI 组件。pKf28资讯网——每日最新资讯28at.com

Block 组件是单独封装的布局组件,希望不要因此造成任何理解上的困难。pKf28资讯网——每日最新资讯28at.com

一个组件变成了数据与UI的结合。我们分别将复杂的数据处理逻辑封装在 hook 里,将复杂的UI交互逻辑封装在基础 UI 组件里,在使用时,利用他们的封装结果进行组合,能够简单,高效的组合出复杂的页面,这也是我们在实践中最大的追求pKf28资讯网——每日最新资讯28at.com

这里有些人可能会有一些疑问,我只是把一些逻辑放在了另外的地方,代码量最终不仅没有减少,反而还变多了,这样做的好处真的有那么大吗?当然,因为我们封装的 useFetch 和 List 组件,他们承载了大多数的复杂逻辑,并且只会在最开始的时候编写一次,在以后的使用中,就直接引入使用就行了,这极大的简化了后续的开发工作量,对工作效率的提高非常显著pKf28资讯网——每日最新资讯28at.com

二、进一步思考

此时的封装虽然足够简洁。但是没有考虑复用。因此还需要进一步思考改进。pKf28资讯网——每日最新资讯28at.com

我们来分析一下场景:每一个需要信息展示的页面,基本逻辑都是在初始化时,请求接口,获得数据,然后展示信息。我们可以把不同情况的接口请求抽象成为一个接口,然后基于这个场景来思考不同页面的请求的共性与差异。pKf28资讯网——每日最新资讯28at.com

每个页面都要处理信息展示、异常等逻辑,差异的地方就在于获取数据的 api 函数不一样,他返回的数据内容,数据类型也不一样。pKf28资讯网——每日最新资讯28at.com

不一样的东西作为参数传入,那我们只需要将 api 函数作为参数传入即可。pKf28资讯网——每日最新资讯28at.com

const info = useFetch(searchApi)

不过我们此时还需要考虑的是,为了确保自定义 hook 的返回类型具备完整准确的类型推导,我们还需要约定传入 api 的参数类型与返回类型。pKf28资讯网——每日最新资讯28at.com

因此,在定义 useFetch 时,我们先用 ts 约定 api 的具体类型,因为参数类型和返回值类型在封装时都不确定,只能在具体的实参传入之后才能明确,因此使用两个泛型来分别表示参数类型和返回值类型。pKf28资讯网——每日最新资讯28at.com

type API<T, P>   = (param?: P) => Promise<T>

正常代码不会这样换行,之所以这样只是为了在移动端能够更多的展示代码信息而不用滚动查看。pKf28资讯网——每日最新资讯28at.com

然后在定义 useFetch 时传入这两个泛型即可,完整代码如下:pKf28资讯网——每日最新资讯28at.com

import { useEffect, useState, useRef } from 'react'type API<T, P> = (param?: P) => Promise<T>export default function useFetch<T, P>(api: API<T, P>) {  const param = useRef<P>()  const [list, setList] = useState<T>()  const [error, setError] = useState('')  const [loading, setLoading] = useState(true)  function getList() {    api(param.current).then(res => {      setList(res)      setLoading(false)      setError('')    }).catch(err => {      setLoading(false)      setError(err)    })  }  useEffect(() => {    loading && getList()  }, [loading])  return {     param,     setParam: (p: P) => param.current = p,    list,     error,     loading,     setLoading   }}

因为在使用时,传入的 api 函数已经具备了完善的类型,因此我们这种写法可以借助 ts 内部的自动推导而简化使用时在 ts 上的繁琐。pKf28资讯网——每日最新资讯28at.com

pKf28资讯网——每日最新资讯28at.com

const {  loading,   setLoading,   setParam,  list,  error} = useFetch(searchApi)

虽然在使用层面没有任何 ts 的痕迹,但是返回值的类型已经非常明确。pKf28资讯网——每日最新资讯28at.com

pKf28资讯网——每日最新资讯28at.com

由于在封装过程中我们没有处理默认值的情况,因此返回类型可能为 undefined,这在实践中一定要引起重视。你可以根据实际情况往 useFetch 传入默认值,也可以在使用层面初始化默认值pKf28资讯网——每日最新资讯28at.com

const {  loading,   setLoading,   setParam,  list = [],  error} = useFetch(searchApi)

这样,一个通用,高效,且具备准确类型提示的 hook 就被我们封装好了。pKf28资讯网——每日最新资讯28at.com

在实践过程中,由于不同的团队有不同的需求,你还需要根据自己的需求和项目实际情况做相应的细节调整,切记不要完整套用。pKf28资讯网——每日最新资讯28at.com

三、取舍

由于面试的影响,让不少前端同行错误的把性能当成了实践中最重要的标准。但其实工作中性能并不是最高的优先级。我们往往会在可接受的范围之内,牺牲性能换取其他的便利。pKf28资讯网——每日最新资讯28at.com

例如,多一层函数封装,其实也就意味着执行压力多那么一点点。但是他可能换来的是开发效率的极大提高。pKf28资讯网——每日最新资讯28at.com

因此,在我们的课程案例决策当中,提供的方案并不会把性能当做第一准则,代码的可读性、可维护性、开发效率的优先级都会比性能更高。只要我们在写代码的过程中,非常明确的知道这种方式我们舍弃了什么,得到了什么,你权衡之后,愿意做出这样的取舍,那么这样的方式就是可以使用的。pKf28资讯网——每日最新资讯28at.com

当然,性能依然非常重要,如果你的页面出现了卡顿,我们就应该思考一下,是不是对性能的牺牲有点过了头。pKf28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-51248-0.htmluseEffect 实践案例:自定义 Hook

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 研发提效必备技能:25张图手把手教你基于Docker搭建Maven私服仓库

下一篇: 看完后,你再也不用怕面试问并发编程啦

标签:
  • 热门焦点
  • CSS单标签实现转转logo

    CSS单标签实现转转logo

    转转品牌升级后更新了全新的Logo,今天我们用纯CSS来实现转转的新Logo,为了有一定的挑战性,这里我们只使用一个标签实现,将最大化的使用CSS能力完成Logo的绘制与动画效果。新logo
  • K8S | Service服务发现

    K8S | Service服务发现

    一、背景在微服务架构中,这里以开发环境「Dev」为基础来描述,在K8S集群中通常会开放:路由网关、注册中心、配置中心等相关服务,可以被集群外部访问;图片对于测试「Tes」环境或者
  • 不容错过的MSBuild技巧,必备用法详解和实践指南

    不容错过的MSBuild技巧,必备用法详解和实践指南

    一、MSBuild简介MSBuild是一种基于XML的构建引擎,用于在.NET Framework和.NET Core应用程序中自动化构建过程。它是Visual Studio的构建引擎,可在命令行或其他构建工具中使用
  • 2天涨粉255万,又一赛道在抖音爆火

    2天涨粉255万,又一赛道在抖音爆火

    来源:运营研究社作者 | 张知白编辑 | 杨佩汶设计 | 晏谈梦洁这个暑期,旅游赛道彻底火了:有的「地方」火了&mdash;&mdash;贵州村超旅游收入 1 个月超过 12 亿;有的「博主」火了&m
  • 消费结构调整丨巨头低价博弈,拼多多还卷得动吗?

    消费结构调整丨巨头低价博弈,拼多多还卷得动吗?

    来源:征探财经作者:陈香羽随着流量红利的退潮,电商的存量博弈越来越明显。曾经主攻中高端与品质的淘宝天猫、京东重拾&ldquo;低价&rdquo;口号。而过去与他们错位竞争的拼多多,靠
  • 自研Exynos回归!三星Galaxy S24系列将提供Exynos和骁龙双版本

    自研Exynos回归!三星Galaxy S24系列将提供Exynos和骁龙双版本

    年初,全新的三星Galaxy S23系列发布,包含Galaxy S23、Galaxy S23+和Galaxy S23 Ultra三个版本,全系搭载超频版骁龙8 Gen 2,虽同样采用台积电4nm工艺制
  • 苹果140W USB-C充电器:采用氮化镓技术

    苹果140W USB-C充电器:采用氮化镓技术

    据10 月 30 日 9to5 Mac 消息报道,当苹果推出新的 MacBook Pro 2021 时,该公司还推出了新的 140W USB-C 充电器,附赠在 MacBook Pro 16 英寸机型的盒子里,也支
  • DRAM存储器10月价格下跌,NAND闪存本月价格与上月持平

    DRAM存储器10月价格下跌,NAND闪存本月价格与上月持平

    10月30日,据韩国媒体消息,自今年年初以来一直在上涨的 DRAM 存储器的交易价格仅在本月就下跌了近 10%,此次是全年首次降价,而NAND 闪存本月价格与上月持平。市
  • 苹果MacBook Pro 2021测试:仍不支持平滑滚动

    苹果MacBook Pro 2021测试:仍不支持平滑滚动

    据10月30日9to5 Mac 消息报道,苹果新的 14 英寸和 16 英寸 MacBook Pro 2021 上市后获得了不错的评价,亮点包括行业领先的性能,令人印象深刻的电池续航,精美丰
Top