AntDesign下拉框选择问题
问题复现
这里提供一个最小Demo,页面会有一个模态框,里面是下拉框,当点击下拉选择选项的时候,会发现Modal会刷新,下拉框选择的值也不见了
const CXK: React.FC = () => {
const [selectValue, setSelectValue] = useState();
const ImportModal: React.FC = () => {
return (
<Modal
open={true}
>
<Select
style={{width: "100%"}}
onSelect={(value, option) => {
setSelectValue(value);
}}
options={[
{value: 'jack', label: 'Jack', key: 'jack'},
{value: 'lucy', label: 'Lucy', key: 'lucy'},
]}
/>
</Modal>
);
}
return (
<div>
<ImportModal/>
</div>
);
}
export default CXK;
问题原因
问题的根源在于状态更新 (setSelectValue
) 导致父组件 CXK
的重新渲染,而 CXK
重新渲染时,会重新定义和渲染子组件 ImportModal
。
- 状态更新触发组件重新渲染:
- 当你在
Select
中选择一个选项时,onSelect
回调函数会被触发,进而调用setSelectValue(value)
。这会更新selectValue
的状态。 - 状态更新会使得
CXK
组件重新渲染。这是 React 的默认行为——当组件的状态或属性发生变化时,React 会重新渲染该组件及其子组件。
- 重新定义和渲染子组件:
- 由于
ImportModal
被定义在CXK
函数内部,每次CXK
重新渲染时,ImportModal
也会被重新定义并渲染。这导致Modal
组件被卸载并重新挂载。 - 由于
Modal
重新渲染,Select
组件也会重新渲染,导致你刚刚选择的值和其他状态都被重置。
为什么会重新渲染?
在 React 中,每当父组件重新渲染时,所有子组件(即使它们的属性和状态没有变化)也会重新渲染,除非你使用 React.memo
或 shouldComponentUpdate
(在类组件中)来优化。
在这个例子中,因为 ImportModal
组件是 CXK
函数的一部分,每次 CXK
重新渲染,ImportModal
都会被重新创建。由于 Modal
是一个动态组件(依赖于状态),它的重新创建会导致内部内容(如 Select
组件)被重置。
解决办法
将 ImportModal
提升到组件外部:将 ImportModal
组件定义提升到 CXK
组件之外,避免每次 CXK
渲染时重新定义 ImportModal
。
const ImportModal: React.FC<{ setSelectValue: (value: any) => void }> = ({setSelectValue}) => {
return (
<Modal open={true}>
<Select
style={{width: "100%"}}
onSelect={(value) => {
setSelectValue(value);
}}
options={[
{value: 'jack', label: 'Jack', key: 'jack'},
{value: 'lucy', label: 'Lucy', key: 'lucy'},
]}
/>
</Modal>
);
};
const CXK: React.FC = () => {
const [selectValue, setSelectValue] = useState();
return (
<div>
<ImportModal setSelectValue={setSelectValue}/>
</div>
);
}
export default CXK;
**使用 useMemo
缓存组件:**使用 useMemo
来缓存 ImportModal
组件,确保它只有在相关依赖(如 setSelectValue
函数)发生变化时才会重新创建。
const CXK: React.FC = () => {
const [selectValue, setSelectValue] = useState();
const ImportModal = React.useMemo(() => {
return (
<Modal open={true}>
<Select
style={{ width: "100%" }}
onSelect={(value) => {
setSelectValue(value);
}}
options={[
{ value: 'jack', label: 'Jack', key: 'jack' },
{ value: 'lucy', label: 'Lucy', key: 'lucy' },
]}
/>
</Modal>
);
}, [setSelectValue]);
return (
<div>
{ImportModal}
</div>
);
};
export default CXK;