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

蜕变之始,useEffect 最后一种用法

来源: 责编: 时间:2024-01-02 09:28:32 300观看
导读在 React 官方文档中,对于 useEffect 有这样一句描述:Effects are an escape hatch from the React paradigm。这句话怎么理解呢?我们要结合前面的哲学部分进行思考。React 开发指导思想是数据驱动 UI,因此在 React 程序

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

在 React 官方文档中,对于 useEffect 有这样一句描述:Effects are an escape hatch from the React paradigm。REm28资讯网——每日最新资讯28at.com

这句话怎么理解呢?我们要结合前面的哲学部分进行思考。React 开发指导思想是数据驱动 UI,因此在 React 程序中,我们总是会思考如何设计与 UI 保持一致的数据,把解决问题的重心放在数据逻辑上。REm28资讯网——每日最新资讯28at.com

但是这样的思路并不能应对所有场景。在一些特殊的场景里,我们需要跳出数据驱动 UI 的解题思路,例如为了避免出现性能瓶颈,在高频率的事件监听中,我们会选择直接使用原生 DOM 节点来解决问题。REm28资讯网——每日最新资讯28at.com

意思就是说,如果你想要跳出 React 的环境使用其他的方式开发,可以把 effect 作为一个对外接口。REm28资讯网——每日最新资讯28at.com

官方文档也对此有进一步的解释:REm28资讯网——每日最新资讯28at.com

They let you “step outside” of React and synchronize your components with some external system like a non-React widget, network, or the browser DOMREm28资讯网——每日最新资讯28at.com

这样的 escape hatch 给 React 带来了极大的灵活性。在一个复杂的项目中,我们可以使用 React 解决一部分逻辑,然后使用别的更合适的方案解决另外的问题,这样的灵活性提高了 React 项目的上限。这也是 React 能参与到类似于 Figma 这样庞大、复杂、对性能要求极高的项目中去的原因。REm28资讯网——每日最新资讯28at.com

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

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

一、如何运用

useEffect 第二个参数为一个数组,当我们传入的参数为一个空数组时,表示 effect 仅会在组件首次渲染完成时执行。REm28资讯网——每日最新资讯28at.com

useEffect(effect, [])

只要我们确保当前组件在程序运行过程中相对稳定,不会随时被删除,那么我们就可以在 effect 中获取原生 DOM 节点,并添加绑定事件,回归到原生 DOM 开发思路中去,你甚至可以在这里继续使用 jQuery。REm28资讯网——每日最新资讯28at.com

我们也可以在这里放心使用百度地图 javaScript sdk,从而完整的在 react 项目中嵌入百度地图。REm28资讯网——每日最新资讯28at.com

function App() {  // ...  useEffect(() => {    var mp = new BMapGL.Map('map');     mp.centerAndZoom(new BMapGL.Point(121.491, 31.233), 11);   }, [])  // ...}

或者与 echarts 结合使用。REm28资讯网——每日最新资讯28at.com

function App() {  const main = useRef(null)  useEffect(() => {    var myChart = echarts.init(main.current);    // 指定图表的配置项和数据    var option = {      title: {        text: 'ECharts 入门示例'      },      tooltip: {},      legend: {        data: ['销量']      },      xAxis: {        data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']      },      yAxis: {},      series: [        {          name: '销量',          type: 'bar',          data: [5, 20, 36, 10, 10, 20]        }      ]    };    // 使用刚指定的配置项和数据显示图表。    myChart.setOption(option);  }, [])  return (    <div id="main" ref={main}></div>  )}

其他的例子还有很多,我们这里使用一个案例来进一步感受 React 与原生 DOM 开发结合的方式。REm28资讯网——每日最新资讯28at.com

二、需求

在长页面滚动的过程中,我们常常会在页面的顶部或者旁边,放一个标识组件来告诉用户页面已经滚动到什么位置了。REm28资讯网——每日最新资讯28at.com

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

在滚动的过程中,当前选中状态会自动变化到对应的位置。接下来我们思考一下这样的功能应该如何实现。REm28资讯网——每日最新资讯28at.com

本案例具体要实现的效果如图:REm28资讯网——每日最新资讯28at.com

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

三、实现

实现原理比较简单,我们只需要判断每个元素什么时候应该出现在屏幕中即可。对于原生 DOM 而言,我们可以使用 getBoundingClientRect 来获取元素对象在可视区域中的位置信息。REm28资讯网——每日最新资讯28at.com

本案例中的判断规则非常简单粗暴,因此当同屏出现两个目标元素时会存在规则冲突,实践中的规则设计会更细致一些,判断逻辑也会更复杂。REm28资讯网——每日最新资讯28at.com

React 提供了 useRef 来获取真实 DOM 对象。REm28资讯网——每日最新资讯28at.com

const n1 = useRef<HTMLDivElement>(null)... <div ref={n1}></div>

本案例中的主要内容为我们前面章节中搜索的 demo,因此每个 demo 都是使用 Block 组件来包裹布局。REm28资讯网——每日最新资讯28at.com

<Block className={s.container} title={td.title} desc={td.desc}>  <div className={r.flex}>    <input      className={s.input}      placeholder="请输入您要搜索的内容"      onChange={(e) => setParam(e.target.value)}    />  </div>  <List    list={list}    loading={loading}    error={error}    renderItem={(item) => (      <div key={item} className={s.item}>{item}</div>    )}  /></Block>

但是因为 Block 并没有针对 ref 进行支持,因此我们可以在外面额外套一个 div 作为目标元素。REm28资讯网——每日最新资讯28at.com

import Normal from './search/Normal'... <div ref={n1}>  <Normal /></div>

在页面滚动的过程中,目标元素相对于可视区域的位置会随时发生变化。因此我们可以在 effect 中添加 document 的滚动事件监听。REm28资讯网——每日最新资讯28at.com

useEffect(() => {  document.addEventListener('scroll', () => {    ...  })}, [])

设计一个 state 状态用来对应选中状态。REm28资讯网——每日最新资讯28at.com

const [current, setCurrent] = useState(0)...<div className={current == 0 ? `${s.item} ${s.active}` : s.item}>案例一</div>

这样,我们只需要在滚动过程中,不停的判断每个目标元素和视口的相对位置,当符合条件的目标元素出现在视口时,就设置 current 为对应的值,功能就实现了。REm28资讯网——每日最新资讯28at.com

useEffect(() => {  document.addEventListener('scroll', () => {    const n1y = n1.current?.getBoundingClientRect().y    const n2y = n2.current?.getBoundingClientRect().y    const n3y = n3.current?.getBoundingClientRect().y    const n4y = n4.current?.getBoundingClientRect().y    const n5y = n5.current?.getBoundingClientRect().y    if (n1y > 0 && n1y < window.innerHeight) {      setCurrent(0)    }    if (n2y > 0 && n2y < window.innerHeight) {      setCurrent(1)    }    if (n3y > 0 && n3y < window.innerHeight) {      setCurrent(2)    }    if (n4y > 0 && n4y < window.innerHeight) {      setCurrent(3)    }    if (n5y > 0 && n5y < window.innerHeight) {      setCurrent(4)    }  })}, [])

当我们使用 ref 获取真实 DOM 时,ref 可能会为 null,但是由于 effect 在组件渲染完成之后执行,此时必定能获取到真实 DOM,因此我们使用 ?. 跳过了为 null 的判断。REm28资讯网——每日最新资讯28at.com

const n1y = n1.current?.getBoundingClientRect().y

完整代码如下:REm28资讯网——每日最新资讯28at.com

import { useEffect, useRef, useState } from 'react'import Normal from './search/Normal'import Normal2 from './search/Normal2'import Normal3 from './search/Normal3'import Normal4 from './search/Normal4'import Normal5 from './search/Normal5'import s from './style.module.scss'export default function EffectDemo() {  const [current, setCurrent] = useState(0)  const n1 = useRef<HTMLDivElement>(null)  const n2 = useRef<HTMLDivElement>(null)  const n3 = useRef<HTMLDivElement>(null)  const n4 = useRef<HTMLDivElement>(null)  const n5 = useRef<HTMLDivElement>(null)  useEffect(() => {    document.addEventListener('scroll', () => {      const n1y = n1.current?.getBoundingClientRect().y as number      const n2y = n2.current?.getBoundingClientRect().y as number      const n3y = n3.current?.getBoundingClientRect().y as number      const n4y = n4.current?.getBoundingClientRect().y as number      const n5y = n5.current?.getBoundingClientRect().y as number      if (n1y > 0 && n1y < window.innerHeight) {        setCurrent(0)      }      if (n2y > 0 && n2y < window.innerHeight) {        setCurrent(1)      }      if (n3y > 0 && n3y < window.innerHeight) {        setCurrent(2)      }      if (n4y > 0 && n4y < window.innerHeight) {        setCurrent(3)      }      if (n5y > 0 && n5y < window.innerHeight) {        setCurrent(4)      }    })  }, [])  return (    <div>      <div className={s.nav}>        <div className={current == 0 ? `${s.item} ${s.active}` : s.item}>案例一</div>        <div className={current == 1 ? `${s.item} ${s.active}` : s.item}>案例二</div>        <div className={current == 2 ? `${s.item} ${s.active}` : s.item}>案例三</div>        <div className={current == 3 ? `${s.item} ${s.active}` : s.item}>案例四</div>        <div className={current == 4 ? `${s.item} ${s.active}` : s.item}>案例五</div>      </div>      <div ref={n1}>        <Normal />      </div>      <div ref={n2}>        <Normal2 />      </div>            <div ref={n3}>        <Normal3 />      </div>      <div ref={n4}>        <Normal4 />      </div>            <div ref={n5}>        <Normal5 />      </div>          </div>  )}

四、总结

大量的初级框架开发者被困在「试图让框架解决所有问题」的思想牢笼里,认为学会了框架似乎就完事了。然而事实上,灵活运用 React 的 escape hatch 特性是成为 React 高手的标志之一,他在指引学习者不要把学习目标局限在 React 中,这是我们蜕变的开始。REm28资讯网——每日最新资讯28at.com

不要试图让 React 解决所有问题,让他做擅长的事情。REm28资讯网——每日最新资讯28at.com

接下来我们思考两个问题,一个问题是,在上面的案例中,我并没有移除事件绑定,这样的行为是否会造成内存泄露?第二个问题,我们期望封装一个图片组件,该组件需要支持懒加载的优化特性:只有当图片内容进入到可视区域时,图片才开始加载,这样的图片组件应该如何封装?REm28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-55008-0.html蜕变之始,useEffect 最后一种用法

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

上一篇: Go测试的20个实用建议,你采纳了吗?

下一篇: Vue.js 3.4版本发布:解析速度提升两倍,双向绑定等新功能

标签:
  • 热门焦点
  • 7月安卓手机性能榜:红魔8S Pro再夺榜首

    7月份的手机市场风平浪静,除了红魔和努比亚带来了两款搭载骁龙8Gen2领先版处理器的新机之外,别的也想不到有什么新品了,这也正常,通常6月7月都是手机厂商修整的时间,进入8月份之
  • 5月iOS设备性能榜:M1 M2依旧是榜单前五

    和上个月一样,没有新品发布的iOS设备性能榜的上榜设备并没有什么更替,仅仅只有跑分变化而产生的排名变动,刚刚开始的苹果WWDC2023,推出的产品也依旧是新款Mac Pro、新款Mac Stu
  • 掘力计划第 20 期:Flutter 混合开发的混乱之治

    在掘力计划系列活动第20场,《Flutter 开发实战详解》作者,掘金优秀作者,Github GSY 系列目负责人恋猫的小郭分享了Flutter 混合开发的混乱之治。Flutter 基于自研的 Skia 引擎
  • 十个简单但很有用的Python装饰器

    装饰器(Decorators)是Python中一种强大而灵活的功能,用于修改或增强函数或类的行为。装饰器本质上是一个函数,它接受另一个函数或类作为参数,并返回一个新的函数或类。它们通常用
  • 从零到英雄:高并发与性能优化的神奇之旅

    作者 | 波哥审校 | 重楼作为公司的架构师或者程序员,你是否曾经为公司的系统在面对高并发和性能瓶颈时感到手足无措或者焦头烂额呢?笔者在出道那会为此是吃尽了苦头的,不过也得
  • 品牌洞察丨服务本地,美团直播成效几何?

    来源:17PR7月11日,美团App首页推荐位出现&ldquo;美团直播&rdquo;的固定入口。在直播聚合页面,外卖&ldquo;神枪手&rdquo;直播间、美团旅行直播间、美团买菜直播间等均已上线,同时
  • 微博大门常打开,迎接海外画师漂洋东渡

    作者:互联网那些事&ldquo;起猛了,我能看得懂日语了&rdquo;。&ldquo;为什么日本人说话我能听懂?&rdquo;&ldquo;中文不像中文,日语不像日语,但是我竟然看懂了&rdquo;&hellip;&hell
  • 三星电子Q2营收60万亿韩元 存储业务营收同比仍下滑超过50%

    7月27日消息,据外媒报道,从三星电子所发布的财报来看,他们主要利润来源的存储芯片业务在今年二季度仍不乐观,营收同比仍在大幅下滑,所在的设备解决方案
  • 最薄的14英寸游戏笔记本电脑 Alienware X14已可以购买

    2022年1月份在国际消费电子展(CES2022)上首次亮相的Alienware新品——Alienware X14现在已经可以购买了,这款笔记本电脑被誉为世界上最薄的 14 英寸游戏笔
Top