为什么 React 组件卸载但 jsx 不卸载
Why does a React component unmount but jsx doesn't
我最近遇到了这种行为,并试图了解其原因。基本上,到目前为止我注意到的是 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 卸载子组件?
我认为你的问题是关于两个按钮行为的区别,一个在点击重新渲染父组件后返回到零,另一个则没有。
首先,我们应该了解一个函数组件的life-cycle,render在执行每一次状态变化。
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}
</>
);
}
我最近遇到了这种行为,并试图了解其原因。基本上,到目前为止我注意到的是 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 卸载子组件?
我认为你的问题是关于两个按钮行为的区别,一个在点击重新渲染父组件后返回到零,另一个则没有。
首先,我们应该了解一个函数组件的life-cycle,render在执行每一次状态变化。
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}
</>
);
}