我最近遇到这种行为,并试图了解其原因。基本上,到目前为止我注意到的是,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}
</>
);
}