react hook应用详解+diff 理解 + 父子组件渲染

文章目录

  • [React Hook函数全解](#React Hook函数全解)
    • [1. useState](#1. useState)
    • [2. useEffect](#2. useEffect)
    • [3. useContext](#3. useContext)
    • [4. useReducer](#4. useReducer)
    • [5. useCallback](#5. useCallback)
    • [6. useMemo](#6. useMemo)
    • [7. useRef](#7. useRef)
    • [8. useImperativeHandle](#8. useImperativeHandle)
    • [9. useLayoutEffect](#9. useLayoutEffect)
    • [10. useDebugValue](#10. useDebugValue)
  • React渲染更新原理
    • [1. 虚拟DOM(Virtual DOM)](#1. 虚拟DOM(Virtual DOM))
    • [2. 协调(Reconciliation)](#2. 协调(Reconciliation))
    • [3. 批量更新和DOM操作](#3. 批量更新和DOM操作)
  • [React Hook函数的运行调用](#React Hook函数的运行调用)
  • React实现MVVM
  • [技术细节( useEffect 清理监听事件和 定时器)](#技术细节( useEffect 清理监听事件和 定时器))
  • diff算法
  • [react 父子组件生命周期应用及渲染](#react 父子组件生命周期应用及渲染)

React Hook函数全解

1. useState

  • 参数
    • 初始值:可以是任意类型(数字、字符串、对象、数组等),用于设定状态的初始状态。例如const [count, setCount] = useState(0),这里0就是count状态的初始值。
  • 应用实例
jsx 复制代码
import React, { useState } from 'react';
function Counter() {
    const [count, setCount] = useState(0);
    return (
        <div>
            <p>You clicked {count} times</p>
            <button onClick={() => setCount(count + 1)}>
                Click me
            </button>
        </div>
    );
}
  • 应用场景
    • 用于在函数组件中管理简单的状态,如表单输入值、开关状态、计数器等。

2. useEffect

  • 参数
    • 第一个参数:一个函数,在组件挂载后、更新后(如果依赖项改变)和卸载前执行。这个函数用于执行副作用操作,如数据获取、订阅事件、手动修改DOM等。
    • 第二个参数:依赖项数组,用于指定副作用函数在哪些状态或属性变化时重新执行。空数组表示只在组件挂载和卸载时执行一次;如果省略,副作用函数会在每次组件渲染后都执行。
  • 应用实例
jsx 复制代码
import React, { useState, useEffect } from 'react';
function Example() {
    const [count, setCount] = useState(0);
    useEffect(() => {
        document.title = `You clicked ${count} times`;
    }, [count]);
    return (
        <div>
            <p>You clicked {count} times</p>
            <button onClick={() => setCount(count + 1)}>
                Click me
            </button>
        </div>
    );
}
  • 应用场景
    • 数据获取:在组件挂载时从API获取数据并更新状态,如获取用户列表展示在页面上。
    • 事件监听:添加和清除事件监听器,比如在组件挂载时添加window.scroll事件监听器,在组件卸载时清除它,防止内存泄漏。

3. useContext

  • 参数
    • 接收一个Context对象,这个对象是通过React.createContext创建的。例如const value = useContext(MyContext),其中MyContext是已创建的上下文对象。
  • 应用实例
jsx 复制代码
import React, { createContext, useContext, useState } from 'react';
const ThemeContext = createContext();
function App() {
    const [theme, setTheme] = useState('light');
    return (
        <ThemeContext.Provider value={theme}>
            <Toolbar />
        </ThemeContext.Provider>
    );
}
function Toolbar() {
    const theme = useContext(ThemeContext);
    return <button onClick={() => setTheme(theme === 'light'? 'dark' : 'light')}>Toggle Theme</button>;
}
  • 应用场景
    • 跨组件共享数据,避免多层级组件通过props层层传递数据,如主题、用户认证信息等。

4. useReducer

  • 参数
    • 第一个参数:一个reducer函数,它接收当前状态和一个action作为参数,并返回新的状态。例如(state, action) => { switch (action.type) {... } return newState; }
    • 第二个参数:初始状态,和useState的初始值类似。
    • 第三个参数(可选):一个初始化函数,用于惰性初始化状态。
  • 应用实例
jsx 复制代码
import React, { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
    switch (action.type) {
        case 'increment':
            return { count: state.count + 1 };
        case 'decrement':
            return { count: state.count - 1 };
        default:
            return state;
    }
}
function Counter() {
    const [state, dispatch] = useReducer(reducer, initialState);
    return (
        <div>
            <p>Count: {state.count}</p>
            <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
            <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
        </div>
    );
}
  • 应用场景
    • 当状态逻辑比较复杂,如状态更新依赖于前一个状态,或者有多个子状态需要一起更新时,使用useReducer可以使状态管理更清晰。

5. useCallback

  • 参数
    • 第一个参数:一个内联函数,这个函数会被缓存。
    • 第二个参数:依赖项数组,用于指定什么时候重新创建这个缓存的函数。
  • 应用实例
jsx 复制代码
import React, { useState, useCallback } from 'react';
function Parent() {
    const [count, setCount] = useState(0);
    const memoizedCallback = useCallback(() => {
        // 一些复杂的计算或操作
        console.log(count);
    }, [count]);
    return (
        <div>
            <Child callback={memoizedCallback} />
            <button onClick={() => setCount(count + 1)}>Update Count</button>
        </div>
    );
}
function Child({ callback }) {
    // 使用传入的回调函数
    return <button onClick={callback}>Execute Callback</button>;
}
  • 应用场景
    • 优化子组件的重新渲染,当传递给子组件的回调函数依赖的状态没有改变时,避免子组件因为函数引用变化而重新渲染。

6. useMemo

  • 参数
    • 第一个参数:一个函数,用于计算一个值。
    • 第二个参数:依赖项数组,用于指定什么时候重新计算这个值。
  • 应用实例
jsx 复制代码
import React, { useState, useMemo } from 'react';
function ExpensiveComponent({ a, b }) {
    const result = useMemo(() => {
        // 复杂的计算
        console.log('Calculating...');
        return a * b;
    }, [a, b]);
    return <div>Result: {result}</div>;
}
function App() {
    const [a, setA] = useState(2);
    const [b, setB] = useState(3);
    return (
        <div>
            <ExpensiveComponent a={a} b={b} />
            <button onClick={() => setA(a + 1)}>Update A</button>
            <button onClick={() => setB(b + 1)}>Update B</button>
        </div>
    );
}
  • 应用场景
    • 用于缓存计算结果,避免在每次组件渲染时都进行昂贵的计算,只有当依赖项改变时才重新计算。

7. useRef

  • 参数
    • 初始值:可以是任何类型的值,用于初始化ref对象的current属性。
  • 应用实例
jsx 复制代码
import React, { useRef, useEffect } from 'react';
function TextInputWithFocusButton() {
    const inputEl = useRef(null);
    const onButtonClick = () => {
        inputEl.current.focus();
    };
    return (
        <>
            <input ref={inputEl} type="text" />
            <button onClick={onButtonClick}>Focus the input</button>
        </>
    );
}
  • 应用场景
    • 访问DOM元素或保存一个可变的值,在组件的整个生命周期内保持不变,如存储定时器ID、表单元素引用等。

8. useImperativeHandle

  • 参数
    • 第一个参数:一个ref对象,通常是通过useRef创建的。
    • 第二个参数:一个函数,用于定义暴露给父组件的实例值。
    • 第三个参数(可选):依赖项数组,用于指定什么时候重新定义暴露的值。
  • 应用实例
jsx 复制代码
import React, { forwardRef, useImperativeHandle, useState } from 'react';
const Child = forwardRef((props, ref) => {
    const [count, setCount] = useState(0);
    useImperativeHandle(ref, () => ({
        increment: () => setCount(count + 1)
    }));
    return <div>{count}</div>;
});
function Parent() {
    const childRef = useRef();
    const handleClick = () => {
        childRef.current.increment();
    };
    return (
        <>
            <Child ref={childRef} />
            <button onClick={handleClick}>Increment Child</button>
        </>
    );
}
  • 应用场景
    • 用于在使用forwardRef的情况下,自定义暴露给父组件的子组件实例属性和方法。

9. useLayoutEffect

  • 参数和使用方式类似useEffect
    • 区别在于useLayoutEffect会在所有DOM变更之后同步调用,在浏览器进行绘制之前。而useEffect是在浏览器绘制之后异步调用。
  • 应用场景
    • 当需要在DOM更新后立即读取布局信息并进行同步操作时使用,如获取更新后的DOM元素的位置、尺寸等信息。

10. useDebugValue

  • 参数
    • 第一个参数:要在React开发者工具中显示的值。
    • 第二个参数(可选):一个格式化函数,用于格式化显示的值。
  • 应用场景
    • 用于在React开发者工具中为自定义Hook提供调试信息,方便开发者查看Hook的内部状态。

React渲染更新原理

1. 虚拟DOM(Virtual DOM)

  • React会根据组件的状态和属性构建一个虚拟DOM树。虚拟DOM是一个轻量级的JavaScript对象,它描述了真实DOM应该是什么样子。例如,一个简单的React组件:
jsx 复制代码
function MyComponent() {
    return <div><p>Hello</p></div>;
}

对应的虚拟DOM可能是类似这样的结构:

javascript 复制代码
{
    type: 'div',
    props: {
        children: [
            {
                type: 'p',
                props: {
                    children: 'Hello'
                }
            }
        ]
    }
}
  • 当组件的状态或属性发生变化时,React会重新构建一个新的虚拟DOM树。

2. 协调(Reconciliation)

  • React使用diff算法来比较新旧虚拟DOM树。这个算法有一些优化策略,例如:
    • 不同类型的元素:如果元素类型改变(如从<div>变为<span>),React会认为整个子树都改变了,会销毁旧的DOM节点并创建新的DOM节点。
    • 同类型的元素:React会比较它们的属性和子元素。对于属性,只会更新变化的属性;对于子元素,会采用高效的比较算法来最小化DOM操作。
  • 例如,有以下组件更新:
jsx 复制代码
function MyComponent() {
    const [count, setCount] = useState(0);
    return (
        <div>
            <p>{count}</p>
            <button onClick={() => setCount(count + 1)}>Increment</button>
        </div>
    );
}

count状态改变时,React会构建新的虚拟DOM,比较新旧虚拟DOM,发现<p>标签的内容发生了变化,只会更新<p>标签的文本内容,而不会重新创建整个div和按钮。

3. 批量更新和DOM操作

  • React会根据diff的结果,计算出最小的DOM操作集合,然后批量更新真实DOM。这样可以减少浏览器重排(reflow)和重绘(repaint)的次数,提高性能。例如,在一次更新中如果有多个组件状态变化导致DOM更新,React会将这些更新合并,一次性应用到真实DOM上。

React Hook函数的运行调用

  • Hook函数必须在函数组件的顶层调用。这是为了保证Hook的调用顺序在每次组件渲染时都是一致的。例如,下面是错误的调用方式:
jsx 复制代码
function MyComponent() {
    if (someCondition) {
        const [count, setCount] = useState(0);
    }
    //...
}
  • 每次组件重新渲染时,Hook函数会按照它们在组件中定义的顺序重新执行。React内部通过一个链表结构来管理Hook。在函数组件内部,有一个隐藏的状态指针,每次调用Hook函数时,这个指针会移动到下一个Hook节点。当组件重新渲染时,这个指针又会从头开始,按照相同的顺序调用Hook,从而保证状态的正确更新和副作用的正确执行。

React实现MVVM

  • Model(数据层)
    • 在React中,stateprops可以看作是模型的一部分。state用于存储组件内部的状态,props用于接收外部传入的数据。例如,在一个用户信息展示组件中,state可能存储用户的编辑状态(如是否处于编辑模式),props可能接收用户的基本信息(如姓名、年龄等)。
  • View(视图层)
    • React组件的JSX部分可以看作是视图。它根据stateprops来渲染出用户界面。例如,一个简单的列表组件:
jsx 复制代码
function ListComponent({ items }) {
    return (
        <ul>
            {items.map((item) => (
                <li key={item.id}>{item.name}</li>
            ))}
        </ul>
    );
}

这里itemspropsJSX代码根据items渲染出一个无序列表视图。

  • ViewModel(数据 - 视图绑定层)
    • 在React中,useStateuseEffect等Hook函数可以看作是数据 - 视图绑定的工具。useState用于将state和视图绑定,当state改变时,视图会重新渲染。useEffect可以用于在stateprops变化时执行副作用操作,间接影响视图。例如:
jsx 复制代码
function Counter() {
    const [count, setCount] = useState(0);
    useEffect(() => {
        document.title = `Count: ${count}`;
    }, [count]);
    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={() => setCount(count + 1)}>Increment</button>
        </div>
    );
}

这里useState绑定了count状态和视图中的显示内容,useEffectcount变化时更新文档标题,实现了数据 - 视图的双向绑定。

技术细节( useEffect 清理监听事件和 定时器)

  1. 清理监听事件

    • useEffect中返回一个清理函数来处理事件监听器的清理。当组件卸载或者useEffect的依赖项发生变化重新执行时,这个清理函数会被调用。
    • 例如,当组件挂载时添加一个windowresize事件监听器,在组件卸载或者依赖项变化时需要移除这个监听器,以避免内存泄漏。
    • 以下是一个示例代码:
    jsx 复制代码
    import React, { useState, useEffect } from 'react';
    
    function ResizeComponent() {
        const [windowWidth, setWindowWidth] = useState(window.innerWidth);
    
        useEffect(() => {
            const handleResize = () => {
                setWindowWidth(window.innerWidth);
            };
            window.addEventListener('resize', handleResize);
            // 返回清理函数,用于移除事件监听器
            return () => {
                window.removeEventListener('resize', handleResize);
            };
        }, []);
    
        return (
            <div>
                <p>当前窗口宽度: {windowWidth}px</p>
            </div>
        );
    }
    
    export default ResizeComponent;
    • 在这个示例中,useEffect的第一个参数是一个函数,在这个函数内部添加了windowresize事件监听器handleResize。这个监听器会在window大小改变时更新windowWidth状态,从而更新组件的显示内容。useEffect的第二个参数是一个空数组[],这意味着这个effect只会在组件挂载和卸载时执行一次。返回的清理函数return () => { window.removeEventListener('resize', handleResize); }用于在组件卸载或者依赖项改变重新执行useEffect时移除resize事件监听器。
  2. 清理定时器

    • 同样是在useEffect中返回一个清理函数来处理定时器的清除。
    • 例如,设置一个定时器每隔一秒更新一次计数器的值,当组件卸载或者依赖项变化重新执行useEffect时,需要清除这个定时器,以防止定时器继续运行导致内存泄漏或者其他错误。
    • 以下是一个示例代码:
    jsx 复制代码
    import React, { useState, useEffect } from 'react';
    
    function TimerComponent() {
        const [count, setCount] = useState(0);
    
        useEffect(() => {
            const timer = setInterval(() => {
                setCount((prevCount) => prevCount + 1);
            }, 1000);
            // 返回清理函数,用于清除定时器
            return () => {
                clearInterval(timer);
            };
        }, []);
    
        return (
            <div>
                <p>计数器: {count}</p>
            </div>
        );
    }
    
    export default TimerComponent;
    • 在这个示例中,useEffect内部通过setInterval创建了一个定时器,每隔一秒会更新count状态。返回的清理函数return () => { clearInterval(timer); }用于在组件卸载或者依赖项改变重新执行useEffect时清除这个定时器。

diff算法

  1. Diff算法的目的

    • React的Diff算法主要用于比较新旧虚拟DOM树,以确定最小的DOM操作集合来更新真实DOM。由于直接操作真实DOM的性能开销较大,Diff算法的高效性有助于减少不必要的DOM操作,如重排(reflow)和重绘(repaint),从而提高应用的性能。
  2. 分层比较策略

    • React将虚拟DOM树按照层级进行比较。比较从树的根节点开始,递归地对每个层级的节点进行比较。
    • 例如,有一个简单的组件树结构如下:
    jsx 复制代码
    <div>
        <p>Hello</p>
        <span>World</span>
    </div>
    • React会先比较最外层的div节点,然后再比较div节点下的pspan子节点。
  3. 不同类型元素的比较

    • 当发现新旧虚拟DOM树中节点的类型不同时,React会认为该节点及其子节点完全不同。
    • 例如,旧的节点是<div>,新的节点是<span>,React会销毁旧的div节点及其所有子节点,然后创建新的span节点及其子节点。
    • 假设原来的组件是这样的:
    jsx 复制代码
    function OldComponent() {
        return <div><p>Old Content</p></div>;
    }
    • 现在更新为:
    jsx 复制代码
    function NewComponent() {
        return <span><p>New Content</p></span>;
    }
    • React会直接替换整个DOM结构,因为根节点的类型从div变成了span
  4. 同类型元素的比较

    • 对于相同类型的元素,React会比较它们的属性。只有属性发生变化的部分才会更新到真实DOM上。
    • 例如,有一个<input>元素,旧的属性是{type: "text", value: "old value"},新的属性是{type: "text", value: "new value"},React会只更新value属性,而不会重新创建整个input元素。
    • 假设组件中有一个输入框:
    jsx 复制代码
    function InputComponent() {
        const [value, setValue] = useState('Initial');
        useEffect(() => {
            setTimeout(() => {
                setValue('Updated');
            }, 1000);
        }, []);
        return <input type="text" value={value} />;
    }
    • 在这里,当value状态更新时,React会比较新旧input元素(因为类型相同),然后只更新value属性。

    • 同时,对于同类型元素的子节点,React会采用高效的比较策略。如果子节点是列表形式,React会使用key属性来帮助识别每个子节点。

    • 例如,有一个列表组件:

    jsx 复制代码
    function ListComponent() {
        const [items, setItems] = useState(['a', 'b', 'c']);
        useEffect(() => {
            setTimeout(() => {
                setItems(['b', 'c', 'd']);
            }, 1000);
        }, []);
        return (
            <ul>
                {items.map((item, index) => (
                    <li key={index}>{item}</li>
                ))}
            </ul>
        );
    }
    • 在这个例子中,当items数组更新时,React会根据key(这里使用了索引index,但在实际应用中最好使用唯一标识符)来比较每个li子节点。如果key相同,就比较节点内容;如果key不同,就认为是新的节点或者旧节点被删除了。不过,使用索引作为key可能会导致性能问题和错误的更新行为,在可能的情况下,最好使用数据的唯一标识符作为key
  5. Diff算法的优化措施

    • React的Diff算法基于一些假设和优化策略来提高性能。例如,它假设在同一层级上,节点的顺序和类型不会频繁地、大幅度地变化。这种假设使得React可以采用简单而高效的比较策略。
    • 同时,React会尽量复用已有的DOM节点。当节点可以复用(类型相同且大部分属性相同)时,就不会重新创建DOM节点,而是更新节点的属性和内容。这有助于减少DOM操作的数量,从而提高应用的整体性能。

react 父子组件生命周期应用及渲染

  1. 挂载阶段(Mounting)

    • 父组件挂载

      • 当一个React应用启动时,首先会挂载根组件。在挂载过程中,父组件的constructor(如果是类组件)会被调用,用于初始化组件的状态和绑定事件处理函数等。
      • 例如,一个简单的父组件ParentComponent
      jsx 复制代码
      import React, { Component } from 'react';
      import ChildComponent from './ChildComponent';
      class ParentComponent extends Component {
          constructor(props) {
              super(props);
              this.state = {
                  parentData: 'Initial Parent Data'
              };
          }
          componentDidMount() {
              console.log('Parent Component Mounted');
          }
          render() {
              return (
                  <div>
                      <ChildComponent parentData={this.state.parentData} />
                  </div>
              );
          }
      }
      export default ParentComponent;
      • constructor中初始化了parentData状态,然后在componentDidMount生命周期方法中打印出Parent Component Mounted,这个方法会在组件挂载到DOM后被调用,常用于进行数据获取、订阅事件等操作。
    • 子组件挂载

      • 当父组件的render方法返回包含子组件的JSX时,子组件开始挂载。子组件同样会经历constructor(如果是类组件)初始化过程。
      • 例如,ChildComponent
      jsx 复制代码
      import React, { Component } from 'react';
      class ChildComponent extends Component {
          constructor(props) {
              super(props);
              console.log('Child Constructor');
          }
          componentDidMount() {
              console.log('Child Component Mounted');
          }
          render() {
              return (
                  <div>
                      <p>{this.props.parentData}</p>
                  </div>
              );
          }
      }
      export default ChildComponent;
      • constructor中会打印Child Constructor,用于初始化子组件相关的操作。componentDidMount方法会在子组件挂载到DOM后被调用,这里会打印Child Component Mounted
    • 挂载顺序总结

      • 挂载顺序是先父组件的constructor,然后是父组件的render,在父组件render过程中如果有子组件,会先调用子组件的constructor,最后是子组件的componentDidMount,再是父组件的componentDidMount。在上述例子中,控制台输出顺序是:Child Constructor -> Child Component Mounted -> Parent Component Mounted
  2. 更新阶段(Updating)

    • 父组件更新

      • 当父组件的状态或属性发生变化时,父组件会重新渲染。在更新过程中,shouldComponentUpdate(如果定义了这个方法)会首先被调用,用于决定组件是否需要更新。
      • 例如,在ParentComponent中添加一个更新状态的方法:
      jsx 复制代码
      import React, { Component } from 'react';
      import ChildComponent from './ChildComponent';
      class ParentComponent extends Component {
          constructor(props) {
              super(props);
              this.state = {
                  parentData: 'Initial Parent Data'
              };
          }
          componentDidMount() {
              console.log('Parent Component Mounted');
          }
          updateParentData = () => {
              this.setState({
                  parentData: 'Updated Parent Data'
              });
          };
          render() {
              return (
                  <div>
                      <button onClick={this.updateParentData}>Update Parent Data</button>
                      <ChildComponent parentData={this.state.parentData} />
                  </div>
              );
          }
      }
      export default ParentComponent;
      • 当点击Update Parent Data按钮时,parentData状态更新,shouldComponentUpdate(如果有)会被调用。如果这个方法返回true(默认行为),组件会继续更新。
    • 子组件更新

      • 因为父组件的状态或属性变化导致子组件的props变化,子组件也会更新。子组件会先调用componentWillReceiveProps(如果是类组件且定义了这个方法)来接收新的props,然后根据新的props进行更新。
      • 对于ChildComponent,如果定义componentWillReceiveProps方法可以这样:
      jsx 复制代码
      import React, { Component } from 'react';
      class ChildComponent extends ClassComponent {
          constructor(props) {
              super(props);
              console.log('Child Constructor');
          }
          componentWillReceiveProps(nextProps) {
              console.log('Child Will Receive Props');
              console.log('Old Props:', this.props);
              console.log('New Props:', nextProps);
          }
          componentDidUpdate(prevProps, prevState) {
              console.log('Child Component Updated');
              console.log('Previous Props:', prevProps);
              console.log('Current Props:', this.props);
          }
          render() {
              return (
                  <div>
                      <p>{this.props.parentData}</p>
                  </div>
              );
          }
      }
      export default ChildComponent;
      • 当父组件更新导致子组件props变化时,componentWillReceiveProps会被调用,用于处理新的props。然后componentDidUpdate会在子组件更新完成后被调用,用于执行更新后的操作,如检查props或状态的变化情况。
    • 更新顺序总结

      • 当父组件状态或属性更新导致子组件props更新时,更新顺序一般是父组件的shouldComponentUpdate(如果有) -> 父组件的render -> 子组件的componentWillReceiveProps(如果有) -> 子组件的render -> 子组件的componentDidUpdate -> 父组件的componentDidUpdate
  3. 卸载阶段(Unmounting)

    • 子组件卸载

      • 当父组件的render方法不再返回某个子组件时,子组件会被卸载。此时,子组件的componentWillUnmount方法会被调用,用于清理在组件挂载和使用过程中产生的副作用,如清除定时器、取消事件订阅等。
      • 例如,在ParentComponent中添加一个条件渲染,使得ChildComponent可以被卸载:
      jsx 复制代码
      import React, { Component } from 'react';
      import ChildComponent from './ChildComponent';
      class ParentComponent extends Component {
          constructor(props) {
              super(props);
              this.state = {
                  showChild: true
              };
          }
          componentDidMount() {
              console.log('Parent Component Mounted');
          }
          toggleChild = () => {
              this.setState((prevState) => ({
                  showChild:!prevState.showChild
              }));
          };
          render() {
              return (
                  <div>
                      <button onClick={this.toggleChild}>Toggle Child</button>
                      {this.state.showChild && <ChildComponent parentData={this.state.parentData} />}
                  </div>
              );
          }
      }
      export default ParentComponent;
      • ChildComponent中添加componentWillUnmount方法:
      jsx 复制代码
      import React, { Component } from 'react';
      class ChildComponent extends Component {
          constructor(props) {
              super(props);
              console.log('Child Constructor');
          }
          componentWillUnmount() {
              console.log('Child Component Unmounted');
          }
          render() {
              return (
                  <div>
                      <p>{this.props.parentData}</p>
                  </div>
              );
          }
      }
      export default ChildComponent;
      • 当点击Toggle Child按钮,showChild状态改变导致ChildComponent被卸载时,会打印Child Component Unmounted
    • 父组件卸载

      • 当整个应用或者包含父组件的上级组件被卸载时,父组件的componentWillUnmount方法也会被调用,用于清理父组件相关的副作用。
    • 卸载顺序总结

      • 先卸载子组件,调用子组件的componentWillUnmount,然后如果父组件也被卸载,再调用父组件的componentWillUnmount
相关推荐
Gnevergiveup7 分钟前
2024网鼎杯青龙组Web+Misc部分WP
开发语言·前端·python
你不讲 wood26 分钟前
使用 Axios 上传大文件分片上传
开发语言·前端·javascript·node.js·html·html5
Iqnus_1231 小时前
vue下载安装
前端·vue.js·笔记
网安_秋刀鱼1 小时前
CSRF防范及绕过
前端·安全·web安全·网络安全·csrf·1024程序员节
新知图书1 小时前
Django 5企业级Web应用开发实战-分页
前端·python·django
GDAL2 小时前
HTML入门教程8:HTML格式化
前端·html
清灵xmf2 小时前
UI 组件的二次封装
前端·javascript·vue.js·element-plus
聪明的墨菲特i2 小时前
Vue组件学习 | 二、Vuex组件
前端·vue.js·学习·前端框架·1024程序员节
海盗12342 小时前
Web前端开发工具和依赖安装
前端
小何学计算机2 小时前
Nginx配置基于端口的 Web 服务器
服务器·前端·nginx