添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

初始化项目

使用create-react-app初始化项目后创建MyRcFieldForm文件,代码如下

import React, { Component, useEffect } from 'react';
 import Form, { Field } from 'rc-field-form';
//import Form, { Field } from './components/my-rc-field-form/';
import Input from './components/Input';
const nameRules = { required: true, message: '请输入姓名!' };
const passworRules = { required: true, message: '请输入密码!' };
export default function MyRCFieldForm(props) {
  const [form] = Form.useForm();
  const onFinish = (val) => {
    console.log('onFinish', val); //sy-log
  // 表单校验失败执行
  const onFinishFailed = (val) => {
    console.log('onFinishFailed', val); //sy-log
  useEffect(() => {
    console.log('form', form); //sy-log
    form.setFieldsValue({ username: 'default' });
  }, []);
  return (
      <h3>MyRCFieldForm</h3>
      <Form form={form} onFinish={onFinish} onFinishFailed={onFinishFailed}>
        <Field name='username' rules={[nameRules]}>
          <Input placeholder='input UR Username' />
        </Field>
        <Field name='password' rules={[passworRules]}>
          <Input placeholder='input UR Password' />
        </Field>
        <button>Submit</button>
      </Form>
    </div>

Input组件代码

import React from 'react';
const Input = (props) => {
  return <input {...props} />;
class CustomizeInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  render() {
    const { value = '', ...otherProps } = this.props;
    return (
      <div style={{ padding: 10 }}>
        <Input style={{ outline: 'none' }} value={value} {...otherProps} />
      </div>
export default CustomizeInput;

上面文件中的Form,Field组件是从rc-field-form库中引入的,接下来我们要自己实现这两个组件。

原理以及实现思路

让我们先来回顾一下antd4.0中表单组件的使用方法,首先使用Form组件包裹自组件,给Form组件可以添加onFinish,onFieldsChange等事件,在这些事件中可以监听到表单的改变获取到表单值然后做相应的处理。我们还可以使用Form.useForm方法获取表单实例,通过该实例中的方法(setFieldsValue,getFieldsValue,validate)我们可以获取、设置、校验表单值。然后使用Form.Item组件包裹表单组件,给Form.Item组件添加上name,rules等属性。然后当表单被输入是我们可以获取到表单的内容。

const Demo = () => {
	const [form] = Form.useForm();
	const handleChange = values => console.log(values);
	return (
		<Form form={form} onChange={handlChange}>
			<Form.Item name="usename" >
				<Input />
			</Form.Item>
		</Form>

实现思路:首先我们需要将表单组件的输入值存储到一个公共的位置 store,然后,暴露出修改方法给表单组件,当表单组件InputonChange事件触发后调用修改方法(setFieldsValue)将新的值更新到store,然后重新触发组件渲染将视图同步。我们通过Form.useForm创建store的实例,然后通过React中的context将实例传递给各个Form.Item组件。在Form.Item组件中我们需要为表单组件Input实现onChange事件,将改变通过context中的实例方法更新到store当中,最后重新渲染自己实现视图同步。

搭出Form、Field组件,useForm自定义hook的架子

  • context文件
const FormContext = React.createContext();
export default FormContext;
import FormContext from './context'
const Form = ({children, form}) => {
	return (
		<FormContext.Provider value={form}>
			<form>{children}</form>
		</FormContext.Provider>
  • Field
import FormContext from './context'
class Field extends Component {
	static contextType = FormContext
	getControled = () => {
		const {name} = this.props;
		return {
			value: '',
			onChange: e => {console.log(e.target.value}
	render() {
		const {children} = this.props;
		const newChildren = React.cloneElement(children, {...this.getControled()})
		return newChildren
  • useForm
class FieldStore {
	constructor() {
		this.store = {};
	getFieldValue = (name) => {return this.store[name]}
	getFieldsValue = () => {return this.store}
	setFieldsValue = (newValues) => {
		this.store = {
			...this.store,
			...newValues
		setFieldValue = (newValues) => {
		this.store = {
			...this.store,
			...newValues
	validate = () => {}
	getForm = () => {
		return {
			getFieldValue: this.getFieldValue,
			getFieldsValue: this.getFieldsValue,
			setFieldsValue: this.setFieldsValue,
			setFieldValue: this.setFieldValue,
			validate: this.validate,
const useForm = (form) => {
	const formRef = React.useRef();
	if (!formRef.current) {
		if (form) {
			formRef.current = form
		} else {
			formRef.current = new FieldStore()
	return [formRef.current]
export default useForm;
  • Field组件
    上面的Field组件还没有实现获取到store里面的value以及当chang事件触发后将值更新到store里面最后自己更新自己实现视图同步的功能,接下来我们一步一步来实现。
    我们已经绑定了context,于是我们可以从表单实例里面获取到操作store的方法,首先在getControled方法中通过getFieldsValue方法获取到该组件组件对应的值赋值给value,然后在onChange事件里面将e.target.value通过setFieldsValue更新store。代码如下
 	getControled = () => {
		const {name} = this.props;
		const {getFieldValue, setFieldsValue} = this.context;
		return {
			value: getFieldValue(name),
			onChange: e => {
				setFieldsValue(e.target.value)

接下来还剩更新自己,我们可以在store里面维护一个实例数组entities获取到所有的表单组件实例,然后在setFieldsValue方法调用时拿到对应的实例调用他的更新方法将其更新,组件自己更新时我们使用到了组件的foruceUpdate方法,由于只需要注册实例一次,我们在Field组件的componentWillMount生命周期当中将该组件的实例也就是this加到entities里面。这里还有一个问题不要忘了,当该组件被隐藏时需要将该其实例从entities里面删掉,所以我们还要在组件的componentWillUnmount方法里取消注册。是不是很简单呢,请看代码~

  • useForm
	constructor() {
		this.store = {};
		this.entities = [];
	registerEntity = (entity) => {
		this.entities.push(entity);
		return () => {
			const name = entity.props.name;
			this.entities = this.entities.filter(item => item !== entity)
			delete this.store[name]
	setFieldsValue = (newValues) => {
		this.store = {
			...this.store,
			...newValues
		this.entities.forEach(entity => {
			const {name} = entity.props
			if (newValues.includes(name)) {
				entity.fourceUpdate()
  • Field
componentWillMount() {
	const {registerEntity} = this.context;
	this.cancelRegister = registerEntity(this);
componentWillUnmount() {
	this.cancelRegister();
fourceUpdate() {
	this.forceUpdate();

到这里我们的表单组件已经基本可以使用了,校验功能我们只需要在validate方法里面拿到对应的校验规则rulesstore里面的值进行校验并返回对应的信息就可以了,这里就不写了。
最后我们来实现一下Form组件的提交方法,antd中的Form组件上有很多的事件例如onFinish, onFinishFailed,onFieldsChange等,我们在store里面再维护一个callbacks对象来存储这些事件。

  • useForm
	constructor() {
		this.store = {};
		this.entities = [];
		this.callbacks = {}
	  setCallback = callback => {
	    this.callbacks = {
	      ...this.callbacks,
	      ...callback
	  submit = () => {
	    console.log("this.", this.fieldEnetities); //sy-log
	    let err = this.validate();
	    // 在这里校验 成功的话 执行onFinish ,失败执行onFinishFailed
	    const {onFinish, onFinishFailed} = this.callbacks;
	    if (err.length === 0) {
	      // 成功的话 执行onFinish
	      onFinish(this.getFiledsValue());
	    } else if (err.length > 0) {
	      // ,失败执行onFinishFailed
	      onFinishFailed(err);
export default function Form({form, children, onFinish, onFinishFailed}) {
  const [formInstance] = useForm(form);
  formInstance.setCallback({
    onFinish,
    onFinishFailed
  });
  return (
      onSubmit={event => {
        event.preventDefault();
        formInstance.submit();
      <FieldContext.Provider value={formInstance}>
        {children}
      </FieldContext.Provider>
    </form>

以上就是antd4中Form、Form.Item组件的基本实现啦,由于上面的代码是我写该文章时现写的可能有错误,下面是完整代码~

import React from 'react';
import FieldContext from './context';
import { useForm } from './useForm';
const Form = ({ children, form }) => {
  const [formInstance] = useForm(form);
  const onSubmit = (e) => {
    e.preventDefault();
    formInstance.submit();
  return (
    <form onSubmit={onSubmit}>
      <FieldContext.Provider value={formInstance}>{children}</FieldContext.Provider>
    </form>
export default Form;
  • Field
import React, { useEffect } from 'react';
import FieldContext from './context';
class Field extends React.PureComponent {
  static contextType = FieldContext;
  componentWillMount() {
    const { registerEneity } = this.context;
    this.cancelRegister = registerEneity(this);
  componentWillUnmount() {
    this.cancelRegister();
  filedFourceUpdate() {
    this.forceUpdate();
  getControled = () => {
    const { getFieldValue, setFieldsValue, getFieldsValue } = this.context;
    const { name } = this.props;
    return {
      value: getFieldValue(name),
      onChange: (e) => {
        setFieldsValue({ [name]: e.target.value });
        console.log(getFieldsValue());
  render() {
    const { children } = this.props;
    const newChildren = React.cloneElement(children, { ...this.getControled() });
    return <>{newChildren}</>;
export default Field;
  • useForm
import React, { useEffect } from 'react';
class formStore {
  constructor() {
    this.store = {};
    this.entities = [];
    this.callbacks = {};
  registerEneity = (entity) => {
    this.entities.push(entity);
    return () => {
      this.entities = this.entities.filter((item) => item !== entity);
      delete this.store[entity.props.name];
  getFieldsValue = () => {
    return this.store;
  setFieldsValue = (newVals) => {
    this.store = {
      ...this.store,
      ...newVals,
    this.entities.forEach((entity) => {
      const name = entity.props.name;
      if (Object.keys(newVals).includes(name)) entity.filedFourceUpdate();
    });
  getFieldValue = (name) => {
    console.log(this);
    return this.store[name];
  setFieldValue = () => {
    console.log('setFieldsValue');
  validate = () => {};
  setCallbacks = (callbacks) => {
    this.callbacks = {
      ...this.callbacks,
      ...callbacks,
  submit = () => {
    console.log(this.store);
  getForm = () => {
    return {
      getFieldsValue: this.getFieldsValue,
      setFieldsValue: this.setFieldsValue,
      getFieldValue: this.getFieldValue,
      setFieldValue: this.setFieldValue,
      validate: this.validate,
      registerEneity: this.registerEneity,
      setCallbacks: this.setCallbascks,
      submit: this.submit,
const useForm = (form) => {
  const formInstance = React.useRef();
  if (!formInstance.current) {
    if (form) {
      formInstance.current = form;
    } else {
      const store = new formStore();
      formInstance.current = store.getForm();
  return [formInstance.current];
export { useForm };

你以为这样就完了吗,不不不。接下来我们看简单看一看antd3中Form组件的实现

antd3中的表单组件

antd3中使用高阶组件的方式实现Form表单,我们简单说一下高阶组件的实现思路,我们使用一个createForm高阶组件,它返回一个类组件,像上面一样我们需要一个地方存储所有的表单值以及创建修改值的方法,我们在createForm里面用this.state存储表单值,并使用getFieldDecorator方法为input组件添加onChange事件,以及valuevaluestate中获取,这样通过setState方法改变值时视图也会更新,当input值改变时通过setState将改变更新到state上面。setFieldsValue、getFieldsValue这些方法很简单就不说了。
代码如下

import React, {Component} from "react";
export default function createForm(Cmp) {
  return class extends Component {
    constructor(props) {
      super(props);
      this.state = {};
      this.options = {};
    handleChange = e => {
      const {name, value} = e.target;
      this.setState({[name]: value});
    getFieldDecorator = (field, option) => InputCmp => {
      this.options[field] = option;
      return React.cloneElement(InputCmp, {
        name: field,
        value: this.state[field] || "",
        onChange: this.handleChange
      });
    setFieldsValue = newStore => {
      this.setState(newStore);
    getFieldsValue = () => {
      return this.state;
    validateFields = callback => {
      let err = [];
      // 校验 检验规则 this.options
      // 校验的值是this.state
      for (let field in this.options) {
        // 判断state[field]是否是undefined
        // 如果是undefind err.push({[field]: 'err})
        if (this.state[field] === undefined) {
          err.push({
            [field]: "err"
          });
      if (err.length === 0) {
        // 校验成功
        callback(null, this.state);
      } else {
        callback(err, this.state);
    getForm = () => {
      return {
        form: {
          getFieldDecorator: this.getFieldDecorator,
          setFieldsValue: this.setFieldsValue,
          getFieldsValue: this.getFieldsValue,
          validateFields: this.validateFields
    render() {
      return <Cmp {...this.props} {...this.getForm()} />;
import React, {Component} from "react";
// import {createForm} from "rc-form";
import createForm from "../components/my-rc-form/";
import Input from "../components/Input";
const nameRules = {required: true, message: "请输入姓名!"};
const passworRules = {required: true, message: "请输入密码!"};
@createForm
class MyRCForm extends Component {
  constructor(props) {
    super(props);
    // this.state = {
    //   username: "",
    //   password: ""
    // };
  componentDidMount() {
    this.props.form.setFieldsValue({username: "default"});
  submit = () => {
    const {getFieldsValue, validateFields} = this.props.form;
    // console.log("submit", getFieldsValue()); //sy-log
    validateFields((err, val) => {
      if (err) {
        console.log("err", err); //sy-log
      } else {
        console.log("校验成功", val); //sy-log
    });
  render() {
    console.log("props", this.props); //sy-log
    // const {username, password} = this.state;
    const {getFieldDecorator} = this.props.form;
    return (
        <h3>MyRCForm</h3>
        {getFieldDecorator("username", {rules: [nameRules]})(
          <Input placeholder="Username" />
        {getFieldDecorator("password", {rules: [passworRules]})(
          <Input placeholder="Password" />
        <button onClick={this.submit}>submit</button>
      </div>
export default MyRCForm;
                    实现antd中Form、Form.Item组件初始化项目使用create-react-app初始化项目后创建MyRcFieldForm文件,代码如下import React, { Component, useEffect } from 'react'; import Form, { Field } from 'rc-field-form';//import Form, { Field } from './components/my-rc-field-form/';import Input fro
 一个非常简单的HOC,用于AntD Form.Item,使其仅显示文本而不显示组件。 当您需要表单的查看模式时,此功能很有用。
 yarn add form-item-view-hoc
这是将此HOC与AntD Form.Item一起使用的示例
 import React from "react" ;
import ReactDOM from "react-dom" ;
import withFormItemView from "form-item-view-hoc" ;
import { Form , Input , Switch } from "antd" ;
const CustomFormItem = withFormItemView ( Form . Item ) ;
function MyForm ( props ) {
  const
				
记住:setFieldsValue的字段要对应得上 解决antdForm组件setFieldsValue的警告 使用antdForm组件setFieldsValue可能会出现You cannot set a form field before rendering a field associated with the value.警告,字面意义去看是说在 render之前设置了表单值的问题。 在使用setFieldsValue给表单Form的某一个filed赋值时,可能掺杂了非表单控件的字段,Form表单在赋值和渲染时发现有些字段无法渲染在现有的字段(因为不存在),所以就会报出这
Antd 组件大部分基于蚂蚁金服的组件react-component。antdreact-component 都是开源项目,阅读其源码可以给我们带来很多收益,比如: 了解各式各样的组件背后的实现思想 怎样去实现一个对开发和用户都友好的组件,即简单易用,便于扩展。 学习一些我们在写业务代码时不太会用到的 React 高级用法 但是阅读过 Antd 源码就会发现,代码量巨大而且...
要获取 `Form.Item` 下的 `Select` 组件的所有选项,可以通过 `React` 的 `ref` 属性来实现。具体步骤如下: 1. 在 `Form.Item` 使用 `ref` 属性创建一个 `ref` 对象,如: <Form.Item label="Label" name="name" ref={selectRef}> <Select> <Option value="value1">Option 1</Option> <Option value="value2">Option 2</Option> <Option value="value3">Option 3</Option> </Select> </Form.Item> 这里的 `selectRef` 是在 `useState` 创建的,如: const [selectRef, setSelectRef] = useState(null); 2. 在 `useEffect` 等待 `Form.Item` 和 `Select` 组件渲染完成后,通过 `selectRef.current.props.children` 来获取 `Select` 组件下的所有 `Option` 组件,如: useEffect(() => { if (selectRef && selectRef.current) { const options = Array.from(selectRef.current.props.children).map(option => { return { value: option.props.value, label: option.props.children console.log(options); }, [selectRef]); 这里使用 `Array.from` 将 `Select` 的子组件转换为数组,然后遍历数组的每个 `Option` 组件,将其值和标签存储在一个对象,最后将所有对象存储在一个数组并输出到控制台。 注意:在 `useEffect` 需要将 `selectRef` 作为依赖项传入,以确保 `Form.Item` 和 `Select` 组件渲染完成后再执行代码。