为什么 React 组件会卸载,而 jsx 不会



我最近遇到这种行为,并试图了解其原因。基本上,到目前为止我注意到的是,React子组件将在其父组件的状态变化时被加载和卸载。但是,包含相同子组件的jsx不会。

我把这个简化的例子放在一起来演示这个行为。

const Child = ({ title }) => {
const [count, setCount] = React.useState(0);
const increment = () => setCount((x) => x + 1);
return (
<button onClick={increment}>
{title} Current count = {count}
</button>
);
};
const App = () => {
const [, setState] = React.useState(false);
const rerender = () => setState((x) => !x);
const ChildWrapper = () => <Child title="Component" />;
const childWrapperJsx = <Child title="jsx" />;
return (
<div>
<button onClick={rerender}>Re render parent</button>
<br />
<ChildWrapper />
{childWrapperJsx}
</div>
);
}

const domContainer = document.querySelector('#root');
const root = ReactDOM.createRoot(domContainer);
const e = React.createElement;
root.render(e(App));
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>

有人知道这背后的原因吗?在这种情况下,有一种方法可以防止React卸载子组件吗?

我认为你的问题是关于两个按钮的行为之间的差异,一个是在点击渲染父组件后返回零,而另一个不是。

首先,我们应该了解一个函数组件的生命周期,渲染是执行每个状态变化。

function App() {
/**
* it will be executed each render
*/
return (<div />);
}

我们还必须理解之间的区别一个组件和实例化一个组件。

// this is creating a component
const ChildWrapper = () => <Child title="Component" />; 

// this is instantiating a component 
const childWrapperJsx = <Child title="jsx" />;

例如,JSX只是一个将<Child title="jsx" />的语法翻译成React.createElement('div', { title: "jsx" })的工具。为了更好地解释,代码被翻译成这样:

// this is creating a component
const ChildWrapper = () => React.createElement('button', { title: 'Component' });

// this is instantiating a component 
const childWrapperJsx = React.createElement('button', { title: 'jsx' }) ;

不深入洞中。在你的实现中,我们将两个组件都实现到父组件的渲染中,就像这样。

function App() {
const ChildWrapper = () => <Child title="Component" />; 
const childWrapperJsx = <Child title="jsx" />;
return (<div />);
}

现在我们意识到,第一个实现是创建一个新的组件每次渲染,使得react无法记忆树中的组件,这是不可能做到的。

// each render ChildWrapper is a new component. 
const ChildWrapper = () => <Child title="Component" />;

第二个,childWrapperJsx已经是一个实例化和记忆的反应元素。React会在父组件生命周期中保留相同的实例。

根据React的最佳实践,不建议在另一个组件的渲染中创建组件。如果你试图把两个实现都放在组件之外,你将能够看到,在父组件渲染之后,两个组件都不会被卸载。

const ChildWrapper = () => <Child title="Component" />; 
const childWrapperJsx = <Child title="jsx" />;
function App() {

return (
<>
<ChildWrapper />
{childWrapperJsx}
</>
);
}

最新更新