简体   繁体   English

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

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

This question concerns the definition of callback functions inside functional components, with the callback functions being passed down as props to child functional components.这个问题涉及功能组件内部回调函数的定义,回调函数作为道具传递给子功能组件。 I am aware that when defining functions inside functional components and those functions are being passed on as props to child components we should use useCallback .我知道在功能组件中定义函数并且这些函数作为道具传递给子组件时,我们应该使用useCallback In this example I am deliberately not using it.在这个例子中,我故意不使用它。

I have a dummy React app as below:我有一个虚拟的 React 应用程序,如下所示:

App.jsx应用程序.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;

TestComponent.jsx测试组件.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;

TestComponent2.jsx测试组件2.jsx

import { useEffect } from 'react';

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

export default TestComponent2;

Starting the app, the page loads and we are here:启动应用程序,页面加载,我们在这里:

初始应用加载

In the console window everything looks like expected.在控制台窗口中,一切看起来都像预期的那样。 The console.log statements Defining myArrowFunc and myArrowFunc defined ran, and we can see the console.log statements from the useEffect hook within TestComponent and TestComponent2 which ran after they were rendered.定义 myArrowFuncmyArrowFunc 定义console.log语句运行,我们可以从TestComponentTestComponent2中的useEffect挂钩看到console.log语句,它们在渲染后运行。

Now we click on the button Set to x , which invokes the callback function attached to the onClick event of the button.现在我们单击按钮Set to x ,它调用附加到按钮的onClick事件的回调函数。 This callback calls setMessage("x") which updates the state of the App component and consequently triggers a re-render of the component.此回调调用setMessage("x")更新App组件的状态并因此触发组件的重新渲染。

In the console window we can see that the App functional component ran (eg from the console.log("Defining myArrowFunc) ) and one can also see that the useEffect hooks of the child components ran after they were re-rendered.在控制台窗口中,我们可以看到App功能组件运行(例如从console.log("Defining myArrowFunc) ),还可以看到子组件的useEffect挂钩在重新渲染后运行。

在此处输入图像描述

Now, that TestComponent re-rendered is of course understandable.现在,重新渲染的TestComponent当然是可以理解的。 message state is a prop on TestComponent which would cause a re-render of TestComponent .消息状态是TestComponent上的一个道具,它会导致重新渲染TestComponent Also, in the dependency array of TestComponent is specified props (not props.message ), but props is a new object on every render (right?) and hence the reference comparison tells us props has changed and so useEffect runs.此外,在TestComponent的依赖数组中指定了props (不是props.message ),但props是每个渲染上的新对象(对吗?),因此参考比较告诉我们props已更改,因此useEffect运行。

That the useEffect hook of TestComponent2 ran can be understood (right?) from the fact that myArrowFunc is re-defined on each render of App , and so the prop passed to TestComponent2 has in fact changed (reference comparison). TestComponent2useEffect钩子运行可以理解(对吗?)从myArrowFuncApp的每次渲染上重新定义的事实,因此传递给TestComponent2的道具实际上已经改变(参考比较)。

Here comes the part where I become confused: the message state in App now holds "x", and so additional clicks to the button will not change the state, and so should not trigger a re-render of App (and any child components dependant).这是我感到困惑的部分: App中的消息状态现在持有“x”,因此对按钮的额外点击不会改变状态,因此不应触发App的重新渲染(以及任何依赖的子组件)。 When I click the button the following output happens:当我单击按钮时,会发生以下输出:

在此处输入图像描述

The statements console.log('Defining myArrowfunc');语句console.log('Defining myArrowfunc'); and console.log('myArrowfunc defined');console.log('myArrowfunc defined'); ran.跑了。 What does this mean?这是什么意思? Did the component re-render?组件是否重新渲染? If the App function ran, then it should have defined a new myArrowFunc (right?), and since TestComponent2 takes that as a prop, it should have re-rendered and run its useEffect hook?如果App函数运行,那么它应该定义了一个新的myArrowFunc (对吗?),并且由于TestComponent2将其作为道具,它应该重新渲染并运行它的useEffect钩子?

What's interesting as well is that when I again click the button, it does not look like App ran, the output becomes only "Setting message".有趣的是,当我再次单击该按钮时,它看起来不像App运行了,输出只是“设置消息”。

Would very much appreciate an outline / explanation of what exactly is going on here.非常感谢这里到底发生了什么的大纲/解释。

在此处输入图像描述

When you click the button, a console log with setting message run, and then a setState with will make a re-render.当您单击该按钮时,将运行带有设置消息的控制台日志,然后带有 setState 将重新渲染。

At the following click, react is clever enough to see that the state you are setting is the same as before so it doesn't re-render在接下来的点击中,react 足够聪明,可以看到您设置的状态与之前相同,因此不会重新渲染

If you change then the value and click the button, it will re-render, but not for the following cases with the same value.如果您更改该值并单击该按钮,它将重新渲染,但对于以下具有相同值的情况则不会。

To sum up, the firsts console.log won't run if there's not a re-render, and the procedure of react and if default memoizing will prevent it综上所述,如果没有重新渲染,第一个 console.log 将不会运行,并且 react 和 if default memoizing 的过程会阻止它

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

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