马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
useEffect
useEffect 是 React 中的一个 Hook,用于在函数组件中处置惩罚副作用操纵,比方数据获取、订阅变乱、手动修改 DOM 等。以下是 useEffect 的一些告急特性和优点:
1. 替换生命周期方法
模拟类组件的生命周期:useEffect 可以模拟类组件的 componentDidMount、componentDidUpdate 和 componentWillUnmount 方法,答应在组件的差异阶段实行相应的逻辑。
- useEffect(() => {
- // componentDidMount 和 componentDidUpdate 的逻辑
- return () => {
- // componentWillUnmount 的逻辑
- };
- }, [dependencies]);
复制代码 2. 副作用管理
处置惩罚副作用:useEffect 在组件渲染后实行,用于处置惩罚须要在渲染后发生的副作用,比方更新 DOM、设置订阅、发送网络哀求等。
清算副作用:
a. 组件卸载时(Unmount):当组件从界面上被移除时,React 会调用 useEffect 返回的扫除函数。这确保了任何与该组件相干的订阅、计时器或其他副作用在组件不再存在时被精确清算。
b. 在重新实行 effect 之前:假如 useEffect 的依赖项数组中的某个值发生了厘革,React 会在重新实行 effect 之前调用上一次 effect 的扫除函数。这克制了副作用的累积,并确保每次 effect 实行前的环境是干净的。
- useEffect(() => {
- const subscription = someService.subscribe();
- return () => {
- subscription.unsubscribe();
- };
- }, []);
复制代码 3. 依赖项数组
正确控制 effect 实行时机:依赖项数组 [dependencies] 答应你指定 effect 何时重新实行。只有当数组中的依赖项发生厘革时,effect 才会被触发。空数组的话只实行一次,相称于componentDidMount
克制不须要的更新:通过精确设置依赖项数组,可以克制不须要的 effect 实行,进步组件性能。
- useEffect(() => {
- // 仅当 count 变化时执行
- }, [count]);
复制代码 4. 多次使用
拆分逻辑:可以在同一个组件中多次使用 useEffect,每个 effect 钩子可以处置惩罚差异的副作用逻辑,保持代码清晰。
- useEffect(() => {
- // 处理订阅逻辑
- }, []);
- useEffect(() => {
- // 处理数据获取逻辑
- }, [dataId]);
复制代码 5. 与闭包共同
访问最新的 state 和 props:useEffect 内部的函数会捕获到当前渲染周期的 state 和 props,确保副作用操纵基于最新的数据。
6. 支持异步操纵
处置惩罚异步任务:useEffect 可以轻松处置惩罚异步操纵,如数据获取和定时器。
- useEffect(() => {
- async function fetchData() {
- const result = await axios.get('/api/data');
- setData(result.data);
- }
- fetchData();
- }, []);
复制代码 7. 镌汰样板代码
简化代码结构:相比于类组件的生命周期方法,useEffect 在函数组件中使用更加简便,镌汰了样板代码,使代码更易读。
优点总结
简便性:使函数组件可以大概方便地管理副作用,镌汰了对类组件的依赖。
可读性:通过拆分差异的 useEffect,逻辑更清晰,代码更易于维护。
性能优化:通过依赖项数组,克制了不须要的副作用实行,进步了应用性能。
同等性:提供了同一的方式来处置惩罚组件的副作用,镌汰了认知负担。
留意事项
- 精确使用依赖项数组:确保在依赖项数组中包罗全部在 effect 中使用的外部变量,防止由于闭包导致的状态差异等标题。
- 克制无穷循环:假如不慎遗漏依赖项,大概依赖项数组厘革过于频仍,大概导致 effect 无穷实行,造成性能标题。
- 清算函数的告急性:对于订阅、计时器等副作用,肯定要在清算函数中举行清算,防止内存走漏。
示例代码
- 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最新的值?
示例:
- 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 值。
综合表明
- 函数组件的特性:函数组件在每次渲染时都会重新实行函数体,天生新的 count、setCount,以及新的 useEffect 中的函数。
- 闭包捕获:useEffect 中的函数在界说时,捕获了当次渲染的 count 值。当 effect 被实行时,它使用的就是这个捕获的值。
- 最新的状态值:由于每次渲染都会天生新的 count,useEffect 中的函数总是可以大概获取到最新的 count 值。
useLayoutEffect 与 useEffect
1. useLayoutEffect:
在欣赏器完成结构和绘制之前同步实行。
实用于须要读取结构信息并同步修改 DOM 的场景。
会壅闭欣赏器绘制,影响性能,审慎使用。
2. useEffect:
在欣赏器绘制之后异步实行。
不会壅闭渲染,更常用。
示例场景:
使用 useLayoutEffect:丈量 DOM 元素的尺寸或位置,然后立刻修改结构。
createRef和useRef
1. createRef
创建方式:React.createRef()。
实用于:告急在 类组件(Class Components) 中使用。
示例:
- 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,并在整个组件生命周期中保持同等。
2. useRef
创建方式:const refContainer = useRef(initialValue)。
实用于:函数组件(Function Components),由于它是一个 Hook。
示例:
- 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 元素,还可以用于生存恣意可变值,雷同于在类组件中使用实例属性。
不会在组件重新渲染时重新初始化。
特性对比
特性createRefuseRef实用组件范例类组件(Class Components)函数组件(Function Components)调用时机通常在构造函数中调用一次在函数组件内的恣意位置(依照 Hook 规则)每次渲染是否创建新 Ref是,每次调用都会创建新的 Ref 对象否,useRef 返回的对象在整个生命周期中保持稳定用途获取 DOM 元素或子组件实例获取 DOM 元素、生存可变值、存储任何可变数据可恒久化的可变值否,告急用于引用 DOM 或组件实例是,可用于存储恣意可变值,不会触发重新渲染 |