为什么 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}
    </>
  );
}