首页 > 解决方案 > 涉及受控选择组件的 Reactjs 表单提交不起作用

问题描述

在 React 中,涉及受控选择组件的表单提交不起作用。

似乎 React 没有正确设置实际 dom 中的 'selected' 属性。您可以在 React 自己的示例中看到这一点;https://codepen.io/gaearon/pen/JbbEzX?editors=0010从选择组件的 React 文档链接;https://reactjs.org/docs/forms.html#the-select-tag 示例如下所示;

class FlavorForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: 'coconut'};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('Your favorite flavor is: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Pick your favorite flavor:
          <select value={this.state.value} onChange={this.handleChange}>
            <option value="grapefruit">Grapefruit</option>
            <option value="lime">Lime</option>
            <option value="coconut">Coconut</option>
            <option value="mango">Mango</option>
          </select>
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

ReactDOM.render(
  <FlavorForm />,
  document.getElementById('root')
);

如果您运行 Codepen 示例并检查 Chrome 开发人员工具中的选择菜单,您会看到最初没有选择任何选项。现在选择一个选项并再次检查。仍然没有选择任何选项!

在我自己的代码中,我有一个用于选择月份的受控选择菜单。我有一个回调来处理表单的 onSubmit。它首先调用 preventDefault(),然后对表单进行验证,如果验证通过,它会调用 event.currentTarget.submit() 来完成表单的提交。发生的情况是验证成功(特别是 validateChildBirthDate()),因为状态正确,但提交的表单在服务器上验证失败,因为编码表单中的选择菜单没有选择任何内容。我的代码在下面,记住,上面的代码是 React 文档。

这是我的月菜单代码(我使用的是 TypeScript)。注意记录 this.props.month 的 console.log();它记录了预期的月份值,但关联的选项元素从未在真实 dom 中赋予“选择”属性:

import React from 'react';
import "./DatePicker.scss";


export type MonthPickerProps = {
    name: string,       // 'menuId' attribute of select element
    month: number,                  // initially selected month
    onMonthChange(month: number): void   // callback when month changes
}

export default class MonthPicker extends React.Component<MonthPickerProps, {}, any> {

    onMonthChange = (event: React.FormEvent<HTMLSelectElement>): void => {
        const month = Number(event.currentTarget.value);
        if('function' === typeof this.props.onMonthChange) {
            this.props.onMonthChange(month);
        }
    };

    render () {
        console.log(`MonthPicker.render(): month = ${this.props.month}`);

        return (
            <select name={this.props.name}
                    value={this.props.month}
                    onChange={this.onMonthChange}
                    className="monthPickerContainer dateSelector">
                <option key="-1" value="-1">mm</option>
                <option key="0"  value="0" >Jan</option>
                <option key="1"  value="1" >Feb</option>
                <option key="2"  value="2" >Mar</option>
                <option key="3"  value="3" >Apr</option>
                <option key="4"  value="4" >May</option>
                <option key="5"  value="5" >Jun</option>
                <option key="6"  value="6" >Jul</option>
                <option key="7"  value="7" >Aug</option>
                <option key="8"  value="8" >Sep</option>
                <option key="9"  value="9" >Oct</option>
                <option key="10" value="10">Nov</option>
                <option key="11" value="11">Dec</option>
            </select>
        );
    }
}

这是我处理表单组件的 onSubmit 的有状态父组件的代码。验证将通过,因此 event.currentTarget.submit() 将被调用,但服务器上的验证将失败,因为月份选择菜单的输入值始终为 -1。

onRegistrationSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault(); // prevent default so we can validate

    // 1. validate email, if error, show error
    // 2. validate password, if error then show errors
    // 3. validate precon/due date, if error, show error
    // 4. else if no errors, collect fields and submit
    let showErrors = false;
    this.setState({showErrors: false});


    // 1. validate email, if error, show error
    const emailState = validateEmailState(this.state.email.value);
    if(isNotBlank(emailState.errorMessage)) {
        showErrors = true;
        this.setState({showErrors: true, email: emailState});
    }

    // 2. validate password, if error then show errors
    const passwordState = validatePasswordState(this.state.password.value);
    if(isNotBlank(passwordState.errorMessage)) {
        showErrors = true;
        this.setState({showErrors: true, password: passwordState});
    }

    // 3. validate precon/due date, if error, show error
    if(!this.state.isPrecon) {
        const childBirthDateState = validateChildBirthDate(this.state.birthDate.value);
        if(isNotBlank(childBirthDateState.errorMessage)) {
            showErrors = true;
            this.setState({showErrors: true, birthDate: childBirthDateState});
        }
    }

    // 4. else if no errors, collect fields and submit
    if(!showErrors) {
        event.currentTarget.submit();

        console.log("Registration submitted");
    }
};

这是选择组件实现中的一个已知问题吗?您知道的任何解决方法?

我正在使用 react 和 react-dom 16.7

在此先感谢您的帮助。

标签: reactjs

解决方案


React 的工作方式与纯 HTML 表单元素有点不同,在纯 HTML 表单元素中,您将选定的值发布到服务器,在反应中,某些东西的选择由虚拟 DOM 处理,因此您将在组件状态下获得选定的值,当你提交表单,handleSubmit方法触发器,在那里你可以使用状态值来做一些事情,比如一个 API 调用来提交你的数据。

由于您使用event.preventDefault()提交不会按应有的方式工作,因为您停止了事件,反过来,您告诉反应,让我以自己的方式处理。

handleSubmit (event) {
  event.preventDefault();

  const flavor = this.state.value;

  // validate the flavor here
  if (!flavor) return;

  // make a call to an API to do something with the data
  axios.post('/save', { data: flavor }).then({
    // do something else if the call succeeds
    // example: clear the state or show something in the UI like a success notice
  }).catch(e => {
    // or handle the error
    return e
  })
}

推荐阅读