[Ant Design 踩坑]Modal.confirm 中 使用 Form 表单如 Input/TextArea 无法输入

6,494 阅读2分钟

背景

在项目开发中有一个 Table 表格,每行中有一个操作按钮,点击这个操作按钮弹出一个Modal.confirm, Modal.confirm内容是一个输入框,在输入内容后点击确定把输入的内容传递给后端,这是一个相当常见的操作,代码如下:

import { Modal, Button, Input } from 'antd';
import { useState } from 'react';
const ModalTest = () => {
  const [text, setText] = useState<string>('');
  const edit = () => {
    Modal.confirm({
      content: (
        <Input
          value={text}
          onChange={({ target: { value } }) => {
            setText(value);
          }}
        />
      ),
      icon: null,
      onOk: async () => {
        console.log(text, 'text--');
      },
    });
  };
  return (
    <div>
      {/* 测试对比input */}
      <Input
        value={text}
        onChange={({ target: { value } }) => {
          setText(value);
        }}
      />
      <Button onClick={edit}>编辑</Button>
    </div>
  );
};

可以发现在 Modal 的 Input 中是无法正常输入内容的,而在测试对比的Input中则能正常输入。

办法一 ❌

value={text}改为defaultValue={text}能正常输入,但是点击确定之后打印出的text值不对,总是比输入落后一次。

// ...
Modal.confirm({
  content: (
    <Input
      // value={text}
      defaultValue={text}
      onChange={({ target: { value } }) => {
        setText(value);
      }}
    />
  ),
  icon: null,
  onOk: async () => {
    console.log(text, 'text--');
  },
});
// ...

办法二 ❌:

封装一下能不能解决?试了一下还是不行,依然是能输入,但是获取到的数据还是不对

const MyInput = ({ onChange }: any) => {
  const [value, setValue] = useState<string>('');
  useEffect(() => {
    if (onChange) {
      onChange(value);
    }
  }, [value, onChange]);
  return (
    <Input
      value={value}
      onChange={({ target: { value } }) => {
        setValue(value);
      }}
    />
  );
};
const ModalTest = () => {
  const [text, setText] = useState<string>('');
  const onChange = (val: string) => {
    setText(val);
  };
  const edit = () => {
    Modal.confirm({
      content: <MyInput onChange={onChange} />,
      icon: null,
      onOk: async () => {
        console.log(text, 'text--');
      },
    });
  };
  return (
    <div>
      <Button onClick={edit}>编辑</Button>
    </div>
  );
};

export default ModalTest;

办法三 ✅

以上方法都不行,根据 state 更新不及时的表现,我推测Modal.confirm很可能不是一个 React 组件,无法重新渲染,那只能上useRef.current了:

import { useRef } from 'react';
const ModalTest = () => {
  const inputValueRef = useRef<string>('');
  const edit = () => {
    Modal.confirm({
      content: (
        <Input
          onChange={({ target: { value } }) => {
            inputValueRef.current = value;
          }}
        />
      ),
      icon: null,
      onOk: async () => {
        console.log(inputValueRef.current, 'text--');
        // 别忘了最后清空
        inputValueRef.current = '';
      },
    });
  };
  return (
    <div>
      <Button onClick={edit}>编辑</Button>
    </div>
  );
};

结果运行正确 👌。

验证

我在 github 上搜了一下,似乎遇到这个问题的人还挺多的,比如:github issuegithub issuegithub issue

翻源码验证一下自己的想法:

定义的地方:

1.jpg

实现的地方:

2.jpg

可以看到从定义到实现上都是普通函数,而不是一个组件,因此在里面输入的数据就无法实现更新。

后来在另一个issue里面发现,作者已经解释了问题原因 ⬇️,但是大家似乎都不满意...

3.jpg