跳到主要内容

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

  1. 状态更新触发组件重新渲染
  • 当你在 Select 中选择一个选项时,onSelect 回调函数会被触发,进而调用 setSelectValue(value)。这会更新 selectValue 的状态。
  • 状态更新会使得 CXK 组件重新渲染。这是 React 的默认行为——当组件的状态或属性发生变化时,React 会重新渲染该组件及其子组件。
  1. 重新定义和渲染子组件
  • 由于 ImportModal 被定义在 CXK 函数内部,每次 CXK 重新渲染时,ImportModal 也会被重新定义并渲染。这导致 Modal 组件被卸载并重新挂载。
  • 由于 Modal 重新渲染,Select 组件也会重新渲染,导致你刚刚选择的值和其他状态都被重置。

为什么会重新渲染?

在 React 中,每当父组件重新渲染时,所有子组件(即使它们的属性和状态没有变化)也会重新渲染,除非你使用 React.memoshouldComponentUpdate(在类组件中)来优化。

在这个例子中,因为 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;