繁体   English   中英

在 React 功能组件中重新渲染 - 为什么会出现这种行为?

[英]Re-rendering in React functional component - why this behavior?

这个问题涉及功能组件内部回调函数的定义,回调函数作为道具传递给子功能组件。 我知道在功能组件中定义函数并且这些函数作为道具传递给子组件时,我们应该使用useCallback 在这个例子中,我故意不使用它。

我有一个虚拟的 React 应用程序,如下所示:

应用程序.jsx

import { useState, useCallback } from 'react';
import './App.css';
import TestComponent from './components/TestComponent';
import TestComponent2 from './components/TestComponent2';

function App() {

    const [message, setMessage] = useState("");
    console.log('Defining myArrowfunc');
    const myArrowFunc = () => console.log('Hello!');
    console.log('myArrowfunc defined');

    return (
        <>
            <TestComponent message={message} myArrowFunc={myArrowFunc} />
            <TestComponent2 myArrowFunc={myArrowFunc} />
            <input
                type="text"
                value={message}
                onChange={e => setMessage(e.target.value)}
            />
            <button
                type="button"
                onClick={() => {
                    console.log('Setting message');
                    setMessage("x");
            }}>Set to x</button>
        </>
    );
}

export default App;

测试组件.jsx

import { useEffect } from 'react';

function TestComponent(props) {
    
    useEffect(() => {
        console.log(`TestComponent.useEffect, props.message = ${props.message}`);
    }, [props]);
    
    return <h2>TestComponent - {props.message}</h2>;
}

export default TestComponent;

测试组件2.jsx

import { useEffect } from 'react';

function TestComponent2(props) {
    
    useEffect(() => {
        console.log(`TestComponent2.useEffect`);
    }, [props.myArrowFunc]);
    
    return <h2>TestComponent2 - Placeholder</h2>;
}

export default TestComponent2;

启动应用程序,页面加载,我们在这里:

初始应用加载

在控制台窗口中,一切看起来都像预期的那样。 定义 myArrowFuncmyArrowFunc 定义console.log语句运行,我们可以从TestComponentTestComponent2中的useEffect挂钩看到console.log语句,它们在渲染后运行。

现在我们单击按钮Set to x ,它调用附加到按钮的onClick事件的回调函数。 此回调调用setMessage("x")更新App组件的状态并因此触发组件的重新渲染。

在控制台窗口中,我们可以看到App功能组件运行(例如从console.log("Defining myArrowFunc) ),还可以看到子组件的useEffect挂钩在重新渲染后运行。

在此处输入图像描述

现在,重新渲染的TestComponent当然是可以理解的。 消息状态是TestComponent上的一个道具,它会导致重新渲染TestComponent 此外,在TestComponent的依赖数组中指定了props (不是props.message ),但props是每个渲染上的新对象(对吗?),因此参考比较告诉我们props已更改,因此useEffect运行。

TestComponent2useEffect钩子运行可以理解(对吗?)从myArrowFuncApp的每次渲染上重新定义的事实,因此传递给TestComponent2的道具实际上已经改变(参考比较)。

这是我感到困惑的部分: App中的消息状态现在持有“x”,因此对按钮的额外点击不会改变状态,因此不应触发App的重新渲染(以及任何依赖的子组件)。 当我单击按钮时,会发生以下输出:

在此处输入图像描述

语句console.log('Defining myArrowfunc'); console.log('myArrowfunc defined'); 跑了。 这是什么意思? 组件是否重新渲染? 如果App函数运行,那么它应该定义了一个新的myArrowFunc (对吗?),并且由于TestComponent2将其作为道具,它应该重新渲染并运行它的useEffect钩子?

有趣的是,当我再次单击该按钮时,它看起来不像App运行了,输出只是“设置消息”。

非常感谢这里到底发生了什么的大纲/解释。

在此处输入图像描述

当您单击该按钮时,将运行带有设置消息的控制台日志,然后带有 setState 将重新渲染。

在接下来的点击中,react 足够聪明,可以看到您设置的状态与之前相同,因此不会重新渲染

如果您更改该值并单击该按钮,它将重新渲染,但对于以下具有相同值的情况则不会。

综上所述,如果没有重新渲染,第一个 console.log 将不会运行,并且 react 和 if default memoizing 的过程会阻止它

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM