React的基础API介绍(一)

目录

useEffect

useEffect 是 React 中的一个 Hook,用于在函数组件中处理副作用操作,例如数据获取、订阅事件、手动修改 DOM 等。以下是 useEffect 的一些主要特性和优点:

  1. 替代生命周期方法
    模拟类组件的生命周期:useEffect 可以模拟类组件的 componentDidMount、componentDidUpdate 和 componentWillUnmount 方法,允许在组件的不同阶段执行相应的逻辑。
javascript 复制代码
useEffect(() => {
  // componentDidMount 和 componentDidUpdate 的逻辑
  return () => {
    // componentWillUnmount 的逻辑
  };
}, [dependencies]);
  1. 副作用管理
    处理副作用:useEffect 在组件渲染后执行,用于处理需要在渲染后发生的副作用,例如更新 DOM、设置订阅、发送网络请求等。

清理副作用:
a. 组件卸载时(Unmount):当组件从界面上被移除时,React 会调用 useEffect 返回的清除函数。这确保了任何与该组件相关的订阅、计时器或其他副作用在组件不再存在时被正确清理。

b. 在重新执行 effect 之前:如果 useEffect 的依赖项数组中的某个值发生了变化,React 会在重新执行 effect 之前调用上一次 effect 的清除函数。这避免了副作用的累积,并确保每次 effect 执行前的环境是干净的。

javascript 复制代码
useEffect(() => {
  const subscription = someService.subscribe();
  return () => {
    subscription.unsubscribe();
  };
}, []);
  1. 依赖项数组
    精确控制 effect 执行时机:依赖项数组 [dependencies] 允许你指定 effect 何时重新执行。只有当数组中的依赖项发生变化时,effect 才会被触发。空数组的话只执行一次,相当于componentDidMount

避免不必要的更新:通过正确设置依赖项数组,可以避免不必要的 effect 执行,提高组件性能。

javascript 复制代码
useEffect(() => {
  // 仅当 count 变化时执行
}, [count]);
  1. 多次使用
    拆分逻辑:可以在同一个组件中多次使用 useEffect,每个 effect 钩子可以处理不同的副作用逻辑,保持代码清晰。
javascript 复制代码
useEffect(() => {
  // 处理订阅逻辑
}, []);

useEffect(() => {
  // 处理数据获取逻辑
}, [dataId]);
  1. 与闭包配合
    访问最新的 state 和 props:useEffect 内部的函数会捕获到当前渲染周期的 state 和 props,确保副作用操作基于最新的数据。
  2. 支持异步操作
    处理异步任务:useEffect 可以轻松处理异步操作,如数据获取和定时器。
javascript 复制代码
useEffect(() => {
  async function fetchData() {
    const result = await axios.get('/api/data');
    setData(result.data);
  }
  fetchData();
}, []);
  1. 减少样板代码
    简化代码结构:相比于类组件的生命周期方法,useEffect 在函数组件中使用更加简洁,减少了样板代码,使代码更易读。

优点总结

简洁性 :使函数组件能够方便地管理副作用,减少了对类组件的依赖。
可读性 :通过拆分不同的 useEffect,逻辑更清晰,代码更易于维护。
性能优化 :通过依赖项数组,避免了不必要的副作用执行,提高了应用性能。
一致性:提供了统一的方式来处理组件的副作用,减少了认知负担。

注意事项

  1. 正确使用依赖项数组:确保在依赖项数组中包含所有在 effect 中使用的外部变量,防止因为闭包导致的状态不一致问题。
  2. 避免无限循环:如果不慎遗漏依赖项,或者依赖项数组变化过于频繁,可能导致 effect 无限执行,造成性能问题。
  3. 清理函数的重要性:对于订阅、计时器等副作用,一定要在清理函数中进行清理,防止内存泄漏。

示例代码

javascript 复制代码
import React, { useState, useEffect } from 'react';

function ChatRoom({ roomId }) {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    // 订阅聊天信息
    const subscription = ChatAPI.subscribeToRoom(roomId, (newMessage) => {
      setMessages((prevMessages) => [...prevMessages, newMessage]);
    });

    // 清理函数,取消订阅
    return () => {
      ChatAPI.unsubscribeFromRoom(roomId, subscription);
    };
  }, [roomId]); // 当 roomId 变化时,重新订阅

  return (
    <div>
      {messages.map((message) => (
        <p key={message.id}>{message.content}</p>
      ))}
    </div>
  );
}

在上述示例中:

使用了 useEffect 来订阅和取消订阅聊天信息。

依赖项数组 [roomId] 确保当房间 ID 变化时,重新执行 effect。

返回的清理函数确保在组件卸载或 roomId 变化时,取消之前的订阅。

useEffetct是如何拿到变量count最新的值?

示例:

javascript 复制代码
function Counter() {  
  const [count, setCount] = useState(0);    

  useEffect(() => {   
    document.title = `You clicked ${count} times`;  
  });  

  return (    
    <div>      
      <p>You clicked {count} times</p>      
      <button onClick={() => setCount(count + 1)}>        
        Click me      
      </button>   
    </div>  
  );
}

useEffect 中的函数能够读取到最新的 count 状态值,主要是由于 JavaScript 中的闭包机制和 React 函数组件的渲染逻辑。

  1. 每次渲染都会创建新的函数作用域
    新的渲染上下文:在 React 中,每当组件的状态或属性发生变化时,组件都会重新渲染。对于函数组件来说,这意味着组件函数会被重新执行,生成新的渲染结果。
    新的变量和函数:每次渲染都会创建新的作用域,其中的变量(如 count)和函数(如传递给 useEffect 的函数)都是新的。
  2. 闭包捕获最新的状态值
    闭包机制:JavaScript 中,函数会捕获其外部作用域中使用的变量。这意味着在每次渲染时,useEffect 中的函数都会捕获到当次渲染时的 count 值。
    最新的 count 值:由于 count 是在组件函数执行时定义的,每次渲染都会得到最新的状态值。useEffect 中的函数在被调用时,使用的就是这个最新的 count 值。
  3. useEffect 的执行时机
    在渲染后执行:useEffect 中的函数会在组件渲染到屏幕后执行。即使它在渲染期间定义,但执行是在 DOM 更新之后。
    每次渲染后执行:在您的示例中,由于没有指定依赖项数组,useEffect 会在每次渲染后执行,因此每次都会使用最新的 count 值。

综合解释

  1. 函数组件的特性:函数组件在每次渲染时都会重新执行函数体,生成新的 count、setCount,以及新的 useEffect 中的函数。
  2. 闭包捕获:useEffect 中的函数在定义时,捕获了当次渲染的 count 值。当 effect 被执行时,它使用的就是这个捕获的值。
  3. 最新的状态值:由于每次渲染都会生成新的 count,useEffect 中的函数总是能够获取到最新的 count 值。

useLayoutEffect 与 useEffect

  1. useLayoutEffect:

在浏览器完成布局和绘制之前同步执行。

适用于需要读取布局信息并同步修改 DOM 的场景。

会阻塞浏览器绘制,影响性能,谨慎使用。

  1. useEffect:

在浏览器绘制之后异步执行。

不会阻塞渲染,更常用。

示例场景:

使用 useLayoutEffect:测量 DOM 元素的尺寸或位置,然后立即修改布局。

createRef和useRef

  1. createRef
    创建方式:React.createRef()。

适用于:主要在 类组件(Class Components) 中使用。

示例:

javascript 复制代码
import React from 'react';

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }

  componentDidMount() {
    // 通过 this.myRef.current 访问 DOM 元素或子组件实例
    console.log(this.myRef.current);
  }

  render() {
    return <div ref={this.myRef}>Hello World</div>;
  }
}

每次调用 createRef() 都会返回一个新的 Ref 对象。这意味着如果在函数组件中使用 createRef,每次渲染都会创建一个新的 Ref,而不是持久化的。

适用于类组件:在类组件的构造函数中创建一次 Ref,并在整个组件生命周期中保持一致。

  1. useRef
    创建方式:const refContainer = useRef(initialValue)。

适用于:函数组件(Function Components),因为它是一个 Hook。

示例:

javascript 复制代码
import React, { useRef, useEffect } from 'react';

function MyComponent() {
  const myRef = useRef(null);

  useEffect(() => {
    // 通过 myRef.current 访问 DOM 元素或保存任何可变值
    console.log(myRef.current);
  }, []);

  return <div ref={myRef}>Hello World</div>;
}

useRef 返回的 Ref 对象在整个组件生命周期中是持久的。无论组件如何重新渲染,useRef 返回的对象始终指向同一个引用。

不仅可以用于获取 DOM 元素,还可以用于保存任意可变值,类似于在类组件中使用实例属性。

不会在组件重新渲染时重新初始化。

特性 createRef useRef
适用组件类型 类组件(Class Components) 函数组件(Function Components)
调用时机 通常在构造函数中调用一次 在函数组件内的任意位置(遵循 Hook 规则)
每次渲染是否创建新 Ref 是,每次调用都会创建新的 Ref 对象 否,useRef 返回的对象在整个生命周期中保持不变
用途 获取 DOM 元素或子组件实例 获取 DOM 元素、保存可变值、存储任何可变数据
可持久化的可变值 否,主要用于引用 DOM 或组件实例 是,可用于存储任意可变值,不会触发重新渲染
相关推荐
一个处女座的程序猿O(∩_∩)O2 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink5 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者6 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-7 小时前
验证码机制
前端·后端
燃先生._.8 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖9 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235249 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_7482402510 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar10 小时前
纯前端实现更新检测
开发语言·前端·javascript