你可能并不需要useEffect

news2024/12/25 1:40:06

背景

相信大家在写react时都有这样的经历:在项目中使用了大量的useEffect,以至于让我们的代码变得混乱和难以维护。

难道说useEffect这个hook不好吗?并不是这样的,只是我们一直在滥用而已。

在这篇文章中,我将展示怎样使用其他方法来代替useEffect。

什么是useEffect

useEffect允许我们在函数组件中执行副作用。它可以模拟 componentDidMount、componentDidUpdate 和componentWillUnmount。我们可以用它来做很多事情。但是它也是一个非常危险的钩子,可能会导致很多bug。

为什么useEffect是容易出现bug的

来看一个定时器的例子:

import React, { useEffect } from 'react'

const Counter = () => {
  const [count, setCount] = useState(0)

  useEffect(() => {
    const timer = setInterval(() => {
      setCount((c) => c + 1)
    }, 1000)
  })

  return <div>{count}</div>
}
复制代码

这是一个非常常见的例子,但是它是非常不好。因为如果组件由于某种原因重新渲染,就会重新设置定时器。该定时器将每秒调用两次,很容易导致内存泄漏。

怎样修复它?

useRef

import React, { useEffect, useRef } from 'react'

const Counter = () => {
  const [count, setCount] = useState(0)
  const timerRef = useRef()

  useEffect(() => {
    timerRef.current = setInterval(() => {
      setCount((c) => c + 1)
    }, 1000)
    return () => clearInterval(timerRef.current)
  }, [])

  return <div>{count}</div>
}
复制代码

它不会在每次组件重新渲染时设置定时器。但是我们在项目中并不是这么简单的代码。而是各种状态,做各种事情。

你以为你写的useEffect

useEffect(() => {
  doSomething()

  return () => cleanup()
}, [whenThisChanges])
复制代码

实际上是这样的

useEffect(() => {
  if (foo && bar && (baz || quo)) {
    doSomething()
  } else {
    doSomethingElse()
  }
  // 遗忘清理函数。
}, [foo, bar, baz, quo, ...])
复制代码

写了一堆的逻辑,这种代码非常混乱难以维护。

useEffect 到底是用来干啥的

useEffect是一种将React与一些外部系统(网络、订阅、DOM)同步的方法。如果你没有任何外部系统,只是试图用useEffect管理数据流,你就会遇到问题。

有时我们并不需要useEffect

1.我们不需要useEffect转化数据

const Cart = () => {
  const [items, setItems] = useState([])
  const [total, setTotal] = useState(0)

  useEffect(() => {
    setTotal(items.reduce((total, item) => total + item.price, 0))
  }, [items])

  // ...
}
复制代码

上面代码使用useEffect来进行数据的转化,效率很低。其实并不需要使用useEffect。当某些值可以从现有的props或state中计算出来时,不要把它放在状态中,在渲染期间计算它。

const Cart = () => {
  const [items, setItems] = useState([])
  const [total, setTotal] = useState(0)

  const totalNum = items.reduce((total, item) => total + item.price, 0)

  // ...
}
复制代码

如果计算逻辑比较复杂,可以使用useMemo:

const Cart = () => {
  const [items, setItems] = useState([])
  const total = useMemo(() => {
    return items.reduce((total, item) => total + item.price, 0)
  }, [items])

  // ...
}
复制代码

2.使用useSyncExternalStore代替useEffect

useSyncExternalStore

常见方式:

const Store = () => {
  const [isConnected, setIsConnected] = useState(true)

  useEffect(() => {
    const sub = storeApi.subscribe(({ status }) => {
      setIsConnected(status === 'connected')
    })

    return () => {
      sub.unsubscribe()
    }
  }, [])

  // ...
}
复制代码

更好的方式:

const Store = () => {
  const isConnected = useSyncExternalStore(
    storeApi.subscribe,
    () => storeApi.getStatus() === 'connected',
    true
  )

  // ...
}
复制代码

3.没必要使用useEffect与父组件通信

const ChildProduct = ({ onOpen, onClose }) => {
  const [isOpen, setIsOpen] = useState(false)

  useEffect(() => {
    if (isOpen) {
      onOpen()
    } else {
      onClose()
    }
  }, [isOpen])

  return (
    <div>
      <button
        onClick={() => {
          setIsOpen(!isOpen)
        }}
      >
        Toggle quick view
      </button>
    </div>
  )
}
复制代码

更好的方式,可以使用事件处理函数代替:

const ChildProduct = ({ onOpen, onClose }) => {
  const [isOpen, setIsOpen] = useState(false)

const handleToggle = () => {
  const nextIsOpen = !isOpen;
  setIsOpen(nextIsOpen)

  if (nextIsOpen) {
    onOpen()
  } else {
    onClose()
  }
}

  return (
    <div>
      <button
        onClick={}
      >
        Toggle quick view
      </button>
    </div>
  )
}
复制代码

4.没必要使用useEffect初始化应用程序

const Store = () => {
  useEffect(() => {
    storeApi.authenticate()
  }, [])

  // ...
}
复制代码

更好的方式:

方式一:

const Store = () => {
  const didAuthenticateRef = useRef()

  useEffect(() => {
    if (didAuthenticateRef.current) return

    storeApi.authenticate()

    didAuthenticateRef.current = true
  }, [])

  // ...
}
复制代码

方式二:

let didAuthenticate = false

const Store = () => {
  useEffect(() => {
    if (didAuthenticate) return

    storeApi.authenticate()

    didAuthenticate = true
  }, [])

  // ...
}
复制代码

方式三:

if (typeof window !== 'undefined') {
  storeApi.authenticate()
}

const Store = () => {
  // ...
}
复制代码

5.没必要在useEffect请求数据

常见写法

const Store = () => {
  const [items, setItems] = useState([])

  useEffect(() => {
    let isCanceled = false

    getItems().then((data) => {
      if (isCanceled) return

      setItems(data)
    })

    return () => {
      isCanceled = true
    }
  })

  // ...
}
复制代码

更好的方式:

没有必要使用useEffect,可以使用swr:

import useSWR from 'swr'

export default function Page() {
  const { data, error } = useSWR('/api/data', fetcher)

  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>

  return <div>hello {data}!</div>
}
复制代码

使用react-query:

import { getItems } from './storeApi'
import { useQuery, useQueryClient } from 'react-query'

const Store = () => {
  const queryClient = useQueryClient()

  return (
    <button
      onClick={() => {
        queryClient.prefetchQuery('items', getItems)
      }}
    >
      See items
    </button>
  )
}

const Items = () => {
  const { data, isLoading, isError } = useQuery('items', getItems)

  // ...
}
复制代码

没有正式发布的react的 use函数:

function Note({ id }) {
  const note = use(fetchNote(id))
  
  return (
    <div>
      <h1>{note.title}</h1>
      <section>{note.body}</section>
    </div>
  )
}
复制代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/421969.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Spring源码】讲讲Bean的生命周期

1、前言 面试官&#xff1a;“看过Spring源码吧&#xff0c;简单说说Spring中Bean的生命周期” 大神仙&#xff1a;“基本生命周期会经历实例化 -> 属性赋值 -> 初始化 -> 销毁”。 面试官&#xff1a;“......” 2、Bean的生命周期 如果是普通Bean的生命周期&am…

【故障诊断】基于 KPCA 进行降维、故障检测和故障诊断研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【Flowable】Flowable流程设计器

Flowable流程设计器有两种实现方式 Eclipse DesignerFlowable UI应用 1.Eclipse Designer Flowable提供了名为Flowable Eclipse Designer的Eclipse插件&#xff0c;可以用于图形化地建模、测试与部署BPMN 2.0流程。 (1).下载安装Eclipse 去Eclipse官网下载即可&#xff1a…

【数据结构:复杂度】时间复杂度

本节重点内容&#xff1a; 算法的复杂度时间复杂度的概念大O的渐进表示法常见时间复杂度计算举例⚡算法的复杂度 算法在编写成可执行程序后&#xff0c;运行时需要耗费时间资源和空间(内存)资源 。因此衡量一个算法的好坏&#xff0c;一般是从时间和空间两个维度来衡量的&…

光伏发电系统模拟及其发电预测开源python工具pvlib

1. 太阳辐照量模拟 pysolar是一个用于计算太阳位置和辐照量的Python库。它是基于python语言编写的&#xff0c;可以方便地在各种python项目中使用。pysolar主要用于计算太阳的位置、太阳高度角、太阳方位角、日出和日落时间等信息。这些信息可以用于太阳能电池板和太阳能集热器…

Spark SQL实战(04)-API编程之DataFrame

1 SparkSession Spark Core: SparkContext Spark SQL: 难道就没有SparkContext&#xff1f; 2.x之后统一的 package com.javaedge.bigdata.chapter04import org.apache.spark.sql.{DataFrame, SparkSession}object SparkSessionApp {def main(args: Array[String]): Unit …

ChatGPT的发展对客户支持能提供什么帮助?

多数组织认为客户服务是一种开销&#xff0c;实际上还可以将客户服务看成是一种机会。它可以让你在销售后继续推动客户的价值。成功的企业深知&#xff0c;客户服务不仅可以留住客户&#xff0c;还可以增加企业收入。客户服务是被低估的手段&#xff0c;它可以通过推荐、见证和…

linux安装Detectron2

参考官方文档&#xff1a;https://detectron2.readthedocs.io/en/latest/tutorials/install.html 1.使用image拉取docker image链接&#xff1a;https://hub.docker.com/r/pytorch/pytorch/tags?page1&name1.8.1-cuda11.1-cudnn8-devel 左上角红框这里搜索1.8.1-cuda1…

Scala - 时间工具类 LocalDateTime 常用方法整理

目录 一.引言 二.LocalDateTime 获取与格式化 1.获取当前时间 LocalDateTime 2.根据时间戳获取 LocalDateTime 3.指定时间获取 LocalDataTime 4.LocalDataTime 格式化 三.LocalDateTime 读取时间细节 1.获取年-Year 2.获取月-Month 3.获取日-Day 4.获取时-Hour 5.获…

Vue3+vite2 博客前端开发

Vue3vite2 博客前端开发 文章目录Vue3vite2 博客前端开发前言页面展示代码设计卡片设计背景&#xff08;Particles.js粒子效果&#xff09;右侧个人信息与公告内容页友链总结前言 大家是否也想拥有一个属于自己的博客&#xff1f;但是如何去开发博客&#xff0c;怎样去开发一个…

新一代AI带来更大想象空间!上海将打造元宇宙超级场景!

引子 上海市经信委主任吴金城4月12日在“2023上海民生访谈”节目表示&#xff0c;上海将着力建设元宇宙智慧医院、前滩东体元宇宙、张江数字孪生未来之城等元宇宙超级场景。 吴金城说&#xff0c;新一代人工智能将带来更大的想象空间。比如&#xff0c;人工智能和元宇宙数字人的…

实验7---myBatis和Spring整合

实验七 myBatis和Spring整合 一、实验目的及任务 通过该实验&#xff0c;掌握mybatis和spring整合方法&#xff0c;掌握生成mapper实现类的两种生成方式。 二、实验环境及条件 主机操作系统为Win10&#xff0c;Tomcat,j2sdk1.6或以上版本。 三、实验实施步骤 略 四、实验报告内…

wait()、sleep()、notify()的解析

wait()、sleep()、notify()的解析 【&#x1f388;问题1】&#xff1a;wait()、sleep()、notify()有什么作用&#xff1f;【&#x1f388;问题2】&#xff1a;wait()、sleep()的区别&#xff1f;【&#x1f388;问题3】&#xff1a;为什么 wait() 方法不定义在 Thread 中&…

九龙证券|今年最贵新股来了,本周还有超低价新股可申购

本周&#xff08;4月17日—4月21日&#xff09;&#xff0c;截至现在&#xff0c;共有3只新股将进行申购&#xff0c;别离为科创板的晶合集成、创业板的三博脑科、北交所的华原股份。其间华原股份将于周一申购&#xff0c;发行价为3.93元/股&#xff0c;晶合集成将于周四申购&a…

全国青少年软件编程(Scratch)等级考试一级考试真题2023年3月——持续更新.....

一、单选题(共25题&#xff0c;共50分) 1. 下列说法不正确的是&#xff1f;&#xff08; &#xff09; A.可以从声音库中随机导入声音 B.可以录制自己的声音上传 C.可以修改声音的大小 D.不能修改声音的速度 试题解析&#xff1a;针对声音可以进行导入&#xff0c;上传&…

Android 不同分辨率下的Drawable尺寸资源设置

启动器图标 36x36 (0.75x) 用于低密度48x48&#xff08;1.0x 基线&#xff09;用于中密度72x72 (1.5x) 用于高密度96x96 (2.0x) 用于超高密度144x144 (3.0x) 用于超超高密度192x192 (4.0x) 用于超超超高密度&#xff08;仅限启动器图标&#xff1b;请参阅上面的 注&#xff09…

redis 主从模式、哨兵模式、cluster模式的区别

参考&#xff1a; ​https://blog.csdn.net/qq_41071876/category_11284995.html https://blog.csdn.net/weixin_45821811/article/details/119421774 https://blog.csdn.net/weixin_43001336/article/details/122816402 Redis有三种模式&#xff0c;分别是&#xff1a;主…

【C++】STL——用一个哈希表封装出unordered_map和unordered_set

用一个哈希表(桶)封装出unordered_map和unordered_set 文章目录用一个哈希表(桶)封装出unordered_map和unordered_set一、哈希表源码二、哈希函数模板参数的控制三、对上层容器构建仿函数便于后续映射四、string类型无法取模问题五、哈希表默认成员函数实现1.构造函数2.拷贝构造…

【JavaEE】ConcurrentHashMap与Hashtable有什么区别?

博主简介&#xff1a;努力的打工人一枚博主主页&#xff1a;xyk:所属专栏: JavaEE初阶Hashtable、ConcurrentHashMap是使用频率较高的数据结构&#xff0c;它们都是以key-value的形式来存储数据&#xff0c;且都实现了Map接口&#xff0c;日常开发中很多人对其二者之间的区别并…

Jmeter实验

Jmeter实验 启动Jmeter 点击bin目录下的&#xff0c;jmeter进行启动 修改界面语言为中文 发起一个最基本的请求 线程组&#xff0c;Http请求&#xff0c;察看结果树 察看结果树的作用范围 设置请求跟随重定向 响应断言-响应文本 判断响应文本中是否有"百度一…