记第一次见深夜的西溪园区,由Form组件引起的“❄️案”

1,035 阅读3分钟

mmexport1681805356781.jpg

凌晨的阿里巴巴

前言

笔者最近在开发项目的过程中,由于老代码组件封装比较深,多层Form.Item嵌套,出现了修改input的值但是不生效的情况,特此记录一下Form.Item的坑.

来个简单

import { Form, Input } from "antd";
import { useRef } from "react";
import "./App.css";

function App() {
  const formRef = useRef();
 
  return (
    <div className="App" name="form">
      <Form ref={formRef}>
        <Form.Item name="username">
          <Input value={12} />
        </Form.Item>
      </Form>
    </div>
  );
}

这一串代码input框会显示出12这个值吗?答案是否定的.为何? 因为看看官网的这句话:

被设置了 name 属性的 Form.Item 包装的控件,表单控件会自动添加 value(或 valuePropName 指定的其他属性) onChange(或 trigger 指定的其他属性),数据同步将被 Form 接管,这

也就是说你在Form.item里面设置value是无效的.那么要怎么样才能生效呢? setFieldsValue

import { Form, Input } from "antd";
import { useRef } from "react";
import "./App.css";

function App() {
  const formRef = useRef();
 formRef.current.setFieldsValue({
    username: 23,
   });
  return (
    <div className="App" name="form">
      <Form ref={formRef}>
        <Form.Item name="username">
          <Input value={12} />
        </Form.Item>
      </Form>
    </div>
  );
}

到这没毛病吧.

假如你中间加了一层别的元素呢?

import { Form, Input } from "antd";
import { useRef } from "react";
import "./App.css";

function App() {
  const formRef = useRef();
 formRef.current.setFieldsValue({
    username: 23,
   });
  return (
    <div className="App" name="form">
      <Form ref={formRef}>
        <Form.Item name="username">
           <div>
              <Input value={12} />
          </div>
        </Form.Item>
      </Form>
    </div>
  );
}

答案是显示12还是23? ----> 12

也就是说Input不在Form.Item紧跟的子元素的时候,input变成了自己受控了,不受Form的控制了.

原因就是Form.Item只会给他紧跟的子元素增加value和onChange属性,这时候给Div设置value和onChange,就算把value改成了23,有用吗!

那么假如我们封装一个组件呢?

function App() {
  const formRef = useRef();
  if (formRef.current) {
    formRef.current.setFieldsValue({
      username: 23,
    });
  }

  return (
    <div className="App" name="form">
      <Form ref={formRef}>
        <Form.Item name="username">
          <InputTest />
        </Form.Item>
      </Form>
    </div>
  );
}

const InputTest = (props) => {
  return <Input value={12} />;
};

答案是显示12还是23? ----> 12

那么我们知道了.他是会给InputTest这个组件的props增加value和onChange方法,但是Input的value还是自己设置的12,props的value和onChange我们也没使用,因为setFieldsValue也无效.我们打印props看看!

image.png

果然,他默认就有value和onChange.就是antd默认加上的.

如果我们单独封装的组件想不自己设置value和onChange,交给antd保管,那么需要这样写!

function App() {
  const formRef = useRef();
  setTimeout(() => {
    if (formRef.current) {
      formRef.current.setFieldsValue({
        username: 23,
      });
    }
  }, 1000);

  return (
    <div className="App" name="form">
      <Form ref={formRef}>
        <Form.Item name="username">
          <InputTest value={12} />
        </Form.Item>
      </Form>
    </div>
  );
}
const InputTest = (props) => {
  console.log("props", props);

  return <Input {...props}  />;
};

将value和onChange透传给input,这样就能实现Input被antd受控的效果,这时候显示什么?

此处答案是显示12还是23? ----> 23 因为被antd重新赋值了value为23 覆盖了你传的value

function App() {
  const formRef = useRef();
  setTimeout(() => {
    if (formRef.current) {
      formRef.current.setFieldsValue({
        username: 23,
      });
    }
  }, 1000);

  return (
    <div className="App" name="form">
      <Form ref={formRef}>
        <Form.Item name="username">
          <InputTest  />
        </Form.Item>
      </Form>
    </div>
  );
}
const InputTest = (props) => {
  console.log("props", props);

  return <Input {...props} value={12} />;
};

这样呢?

此处答案是显示12还是23? ----> 12 . 因为props里有value=23,但是被你后面设置的value=12覆盖了

function App() {
  const formRef = useRef();
  setTimeout(() => {
    if (formRef.current) {
      formRef.current.setFieldsValue({
        username: 23,
      });
    }
  }, 1000);

  return (
    <div className="App" name="form">
      <Form ref={formRef}>
        <Form.Item name="username">
          <InputTest  />
        </Form.Item>
      </Form>
    </div>
  );
}
const InputTest = (props) => {
  console.log("props", props);

  return <Input value={12} {...props}  />;
};

这样呢?

此处答案是显示12还是23? ----> 23 . 因为props里有value=23,覆盖了你写的value=12

看完value我们再看看onChange! 直接输入值会打印1吗?

function App() {
  const formRef = useRef();
  setTimeout(() => {
    if (formRef.current) {
      formRef.current.setFieldsValue({
        username: 23,
      });
    }
  }, 1000);
  const handleChange = () => {
    console.log("1");
  };
  return (
    <div className="App" name="form">
      <Form ref={formRef}>
        <Form.Item name="username">
          <Input onChange={handleChange} />
        </Form.Item>
      </Form>
    </div>
  );
}

答案是会! 是不是和value不一样了,value是会覆盖的,但是onChange是不会被覆盖的

自己写的组件呢?

function App() {
  const formRef = useRef();
  setTimeout(() => {
    if (formRef.current) {
      formRef.current.setFieldsValue({
        username: 23,
      });
    }
  }, 1000);
  const handleChange = () => {
    console.log("1");
  };
  return (
    <div className="App" name="form">
      <Form ref={formRef}>
        <Form.Item name="username">
          <InputTest onChange={handleChange} />
        </Form.Item>
      </Form>
    </div>
  );
}

const InputTest = (props) => {
  console.log("props", props);

  return <Input />;
};

答案是不会! 因为input上都没OnChange方式你得透传props进去或者自己写onChange

const InputTest = (props) => {
  console.log("props", props);

  return <Input {...props} />; 
  return <Input onChange={xx} />;
  
};

总结

大白话:1.Form.item会给紧跟的子元素加上value和onChange属性 2.value会覆盖,onChange不会覆盖.

原项目代码比这复杂的多,搭建个demo看的更清楚了!❤️