React+TS 从零开始教程(4):useEffect

news/2024/7/16 7:47:40 标签: react.js, javascript, 前端

上一节传送门:React+TS 从零开始教程(3):useState

源码链接:https://pan.quark.cn/s/c6fbc31dcb02

上一节,我们已经学会了React的第一个Hook:useState。 这一节,我们要学习的是另一个非常重要的Hook:useEffect。 Effect在这里,意为副作用? 不论是哪一个Hook,都是写在函数里面的,比如上个章节中useEffect,就是写在一个函数中。

import React, { useState } from "react";
 
const Switch = () => {
  //每一个变量都要单独做一个State
  //useState解构出两个变量,一个是变量名,一个是赋值这个变量的函数
  const [on,setOn] = useState(false)
  return (
    <>
    <h2 onClick={ () => {setOn(!on)}}>
    {on?'关闭':'开启'}
    </h2>
    </>
  )
}
 
export default Switch;

所以,这个副作用,就是函数的副作用。 怎么理解呢?我们知道,函数就是你给我参数,我给你一个确定的返回,这个叫函数。但是呢,有一些操作其实跟函数的返回没有太大的关系,却出现在了函数体中,比如网络请求,订阅事件等,这些跟函数渲染不相关,就可以被认为是副作用。

两种常见的副作用。

不需要清除的副作用

我们希望在React更新DOM之后,发送一些网络请求,手动变更DOM,记录日志等,这些操作在我们函数返回之后,完全可以忽略掉。 这些情形,就是不需要清除的副作用。 总之,当React函数式组件return了一个DOM,并成功在页面更新之后,如果你还想做一些快活的事情,就写在useEffect的第一个回调参数里。 在Switch.tsx中,写上如下代码

//以下的effect操作会在DOM更新之后发生
useEffect(() => {
  document.title = 'DOM更新完毕,Effect发生!'
});

useEffect接收一个回调函数,里面定义了DOM更新完成之后的操作,或者说给组件添加了一个渲染后的副作用。 App.tsx

image.png

刷新页面

image.png

页面的标题发生了变化,符合预期。 注意,useEffect会在每次渲染后都执行1次。 之前用Class组件的时候,分挂载和更新两个钩子,所以我们想用这种DOM更新后的操作的话,就得在这两个钩子中各写一遍逻辑,很麻烦。 现在有了useEffect,就化繁为简了。 而且,非常好懂。

需要清除的副作用

有些副作用是需要被清除的,比如事件的添加。 在 之前做法中,我们是在componentDitMount中添加事件,然后在componentWillUnmount中解绑事件。 现在我们用useEffect,该如何实现呢? 我们新创建一个组件,叫MouseTracker。

image.png

顾名思义,就是一个鼠标位置的追踪器。 然后,引入React依赖

import React, { useEffect, useState } from "react";

声明一下函数组件

const MouseTracker : React.FC = () => {
    
}

然后定义坐标状态

//定义坐标
    const [pos,setPos] = useState({x:0,y:0})

直接return

return (
    <p>X: {pos.x} , Y:{pos.y}</p>
)

导出去

export default MouseTracker;

然后在App.tsx中引入这个组件

image.png

目前这个x和y还是静态的,都是0,我们希望每次鼠标点击的时候,就更新这个坐标。 什么时候更新呢,首先是鼠标点击之后,然后触发x和y的更新,也就是dom的更新。 让我们在MouseTracker中添加一个effect

//事件绑定
useEffect(() => {
    const bindCallback = (e:MouseEvent) => {
        console.log('鼠标点击了')
        setPos({x:e.clientX,y:e.clientY})     
    }
    //绑定click事件
    document.addEventListener('click',bindCallback)
})

现在页面是这样的

image.png

我点一次

image.png

再点一次

image.png

再点一次

image.png

每次点击,都会增加N的effect次数。为什么呢?因为useEffect会在每次dom更新后触发,而我们添加了点击事件后没有及时地去清除,所以之前的点击事件还存在,导致每次更新后,所有的点击事件都触发了effect。 怎么解决呢? 我们看下useEffect的源码

    /**
     * Accepts a function that contains imperative, possibly effectful code.
     *
     * @param effect Imperative function that can return a cleanup function
     * @param deps If present, effect will only activate if the values in the list change.
     *
     * @version 16.8.0
     * @see {@link https://react.dev/reference/react/useEffect}
     */
    function useEffect(effect: EffectCallback, deps?: DependencyList): void;

可以看到,这个函数的第一个参数,callback,并不是一定为void,也允许我们返回一个函数。  @param effect Imperative function that can return a cleanup function 意思很明确了,它允许我们返回一个cleanup function,也就是清除函数。 我们修改一下代码

//事件绑定
useEffect(() => {
    const bindCallback = (e:MouseEvent) => {
        console.log('鼠标事件绑定')
        setPos({x:e.clientX,y:e.clientY})     
    }
    //绑定click事件
    document.addEventListener('click',bindCallback)
    return () => {
        document.removeEventListener('click',bindCallback)
    }
})

我连续点击了四次,符合预期。

image.png

怎么限制Effect

上面的例子告诉我们,只有一重新渲染,effect必执行,这可能会带来一些性能问题,有什么办法对effect做限制嘛? 再来看源码

    /**
     * Accepts a function that contains imperative, possibly effectful code.
     *
     * @param effect Imperative function that can return a cleanup function
     * @param deps If present, effect will only activate if the values in the list change.
     *
     * @version 16.8.0
     * @see {@link https://react.dev/reference/react/useEffect}
     */
    function useEffect(effect: EffectCallback, deps?: DependencyList): void;

这次我们重点看第二个参数 @param deps If present, effect will only activate if the values in the list change. 第二个参数是一个数组,如果数组里面的任何一项发生变化,就会触发effect的执行。 刚才的例子中,我们每次点击鼠标,就会触发一次事件的绑定和卸载,实在是有点太浪费了。 我们希望在组件加载的时候绑定一次就行了,然后在组件卸载的时候再解绑。

useEffect(() => {
    console.log('点击事件绑定')
    const bindCallback = (e:MouseEvent) => {
        console.log('鼠标点击了')
        setPos({x:e.clientX,y:e.clientY})     
    }
    //绑定click事件
    document.addEventListener('click',bindCallback)
    return () => {
        console.log('点击事件解绑')
        document.removeEventListener('click',bindCallback)
    }
},[])

我添加一个空数组,代表这个effect不依赖于任何state和props,只跟组件的安装和卸载有关。 然后修改App.tsx(省略页面其他代码)

import React,{useState} from "react";
import MouseTracker from "./components/MouseTracker";

function App() {
  let [showMouseTracker,setShowMouseTracker] = useState(true)
  return (
    <div className="App">
      <div>
        <button onClick={() => {setShowMouseTracker(!showMouseTracker)}}>
          {showMouseTracker ? '关闭鼠标追踪': '打开鼠标追踪'}
        </button>
      </div>
      {showMouseTracker && <MouseTracker />}
      
    </div>
  );
}

export default App;

刷新页面

image.png

多点几次

image.png

点击关闭按钮

image.png

页面卸载,事件解绑了。 如果你希望这个effect只依赖某一个State,将对应的变量填入这个数组即可,有多个就填多个。

本节,我们学会了useEffect的用法。


http://www.niftyadmin.cn/n/5542963.html

相关文章

矢量绘图设计Sketch中文 Sketch直装安装包

Sketch是一款专为UI设计师和UX专家打造的矢量图形设计软件&#xff0c;以其简洁的界面、强大的功能和高效的协作能力而闻名。Sketch支持快速创建高质量的UI界面、图标、图形和插画&#xff0c;其矢量绘图工具让设计细节更加精准。同时&#xff0c;Sketch内置丰富的插件和组件库…

centos7|操作系统|低版本的OpenSSH升级到最新版本OpenSSH-9.8.p1

前言&#xff1a; 1、 OpenSSH是什么 OpenSSH 是 SSH &#xff08;Secure SHell&#xff09; 协议的免费开源实现。SSH协议族可以用来进行远程控制&#xff0c; 或在计算机之间传送文件。而实现此功能的传统方式&#xff0c;如telnet(终端仿真协议)、 rcp ftp、 rlogin、rsh都…

记录第一次使用air热更新golang项目

下载 go install github.com/cosmtrek/airlatest 下载时提示&#xff1a; module declares its path as: github.com/air-verse/air but was required as: github.com/cosmtrek/air 此时&#xff0c;需要在go.mod中加上这么一句&#xff1a; replace github.com/cosmtrek/air &…

【JavaScript脚本宇宙】解锁前端开发必备利器:深入了解6种JavaScript库

探索JavaScript库世界&#xff1a;模块加载与数据绑定的奇妙之旅 前言 在现代Web开发中&#xff0c;JavaScript库扮演着关键的角色&#xff0c;帮助开发人员更高效地构建应用程序。本文将介绍几个常用的模块加载库及相关主题的JavaScript库&#xff0c;包括Knockout、MobX、V…

【Threejs进阶教程-优化篇】4.Vue/React与threejs如何解决冲突和卡顿(续)

Vue/React与threejs如何解决冲突和卡顿-续 使用说明核心思路环境搭建(vuethree)vue运行机制分析业务分离使用threejs做背景 3D模块封装使用ES6的Class来让逻辑性更强Threejs尽量按需引入创建一个类扩展写法本次代码执行顺序 扩展内容添加orbitControls和辅助线解决事件覆盖 与V…

第一周周日总结

题目总结 1.给你一个整数数组 hours&#xff0c;表示以 小时 为单位的时间&#xff0c;返回一个整数&#xff0c;表示满足 i < j 且 hours[i] hours[j] 构成 整天 的下标对 i, j 的数目。 整天 定义为时间持续时间是 24 小时的 整数倍 。 例如&#xff0c;1 天是 24 小时…

蚁剑编码器编写——中篇

看第二个示例 hex编码 木马 <?php foreach($_POST as $k > $v){$_POST[$k]pack("H*", $v);} eval($_POST[ant]); ?>pack(“H*”, $v)是将 $v 转换为为二进制&#xff0c;也就是ASCII解码 编码器 module.exports (pwd, data) > {let ret {};for (…

Oracle 解决4031错误

一、问题描述 什么是4031错误和4031错误产生的原因: 简单一个句话概括: 由于服务器一直在执行大量的硬解析,导致Oracle 的shared pool Free空间碎片过多,大的chunk不足, 当又一条复杂的sql语句要硬解析时, 缺少1个足够大的Free chunk, 通常就会报4031错误. 二、解决方法 临…