使用React.lazy组件时,如何避免数据变化导致子组件重新加载?
问题:React动态加载组件时,父组件状态更新导致子组件重复加载并丢失焦点
我尝试拆分React组件并实现动态加载,创建了两个组件如下:
const PluginComponent = ({ pluginComponent }) => { const DynamicComponent = React.lazy(() => import(`../components/${pluginComponent}.jsx`)); const [formData, setFormData] = useState({ input: '', output: '' }); const handleInputChange = (e) => { const { name, value } = e.target; setFormData((prevData) => ({ ...prevData, [name]: value })) console.log(formData) }; return ( <div> <p>This is the {pluginComponent} plugin component.</p> <React.Suspense fallback={<div>Loading...</div>}> <DynamicComponent formData={formData} handleInputChange={handleInputChange} /> </React.Suspense> </div> ); }; export default PluginComponent; const ExamplePlugin = () => { return ( <> <input name="input" defaultValue={formData.input} onChange={handleInputChange} type="text" className="grow" placeholder="name here..." /> </> ); }; export default ExamplePlugin;
当把formData和handleInputChange逻辑保留在ExamplePlugin中时,一切运行正常。但移至PluginComponent并通过props传递后,每次修改输入值,子组件都会完全重新加载,显示“Loading...”提示,且输入框失去焦点。
请问是否有办法在父组件中存储数据和逻辑,让子组件使用这些内容处理输入,同时避免子组件在每次数据变化时完全重新加载?
解决方案
1. 避免在组件渲染周期内重复创建React.lazy组件
每次PluginComponent重新渲染(比如formData更新时),你都重新执行了React.lazy(() => import(...)),这会导致React认为这是一个全新的组件类型,从而卸载旧组件并重新加载新组件,这就是你看到“Loading...”和焦点丢失的核心原因。
解决方法是用useMemo缓存动态组件,仅当pluginComponent变化时才重新创建:
2. 缓存回调函数减少不必要重渲染
同时,用useCallback缓存handleInputChange,防止每次父组件渲染都生成新的函数引用,导致子组件触发额外重渲染。
3. 修正子组件的props接收与受控模式
你的ExamplePlugin组件没有接收formData和handleInputChange props,需要补充这部分逻辑;同时把defaultValue改成value(受控组件模式),确保输入值和父状态完全同步。
修改后的完整代码:
import React, { useState, useMemo, useCallback, Suspense } from 'react'; const PluginComponent = ({ pluginComponent }) => { // 用useMemo缓存动态组件,仅当pluginComponent变化时重新加载 const DynamicComponent = useMemo(() => { return React.lazy(() => import(`../components/${pluginComponent}.jsx`)); }, [pluginComponent]); const [formData, setFormData] = useState({ input: '', output: '' }); // 用useCallback缓存回调函数 const handleInputChange = useCallback((e) => { const { name, value } = e.target; setFormData((prevData) => ({ ...prevData, [name]: value })); }, []); return ( <div> <p>This is the {pluginComponent} plugin component.</p> <Suspense fallback={<div>Loading...</div>}> <DynamicComponent formData={formData} handleInputChange={handleInputChange} /> </Suspense> </div> ); }; export default PluginComponent; // 子组件接收props并使用受控组件模式 const ExamplePlugin = ({ formData, handleInputChange }) => { return ( <> <input name="input" value={formData.input} onChange={handleInputChange} type="text" className="grow" placeholder="name here..." /> </> ); }; export default ExamplePlugin;
关键说明
useMemo确保只有当pluginComponent变化时才会重新创建动态组件,父组件状态更新(比如formData变化)不会触发组件重新加载。useCallback缓存了handleInputChange的引用,避免每次父组件渲染都传递新的函数给子组件,减少子组件的不必要重渲染。- 子组件改用受控组件模式(
value而非defaultValue),确保输入值和父组件状态完全同步,避免出现值不一致的问题。
内容的提问来源于stack exchange,提问作者Zenth
相关产品推荐
相关产品推荐

