如何以递归方式传递表单中的所有数据?

时间:2016-05-01 03:02:50

标签: reactjs

我有一张表格,看起来像这样:

export default class BookingForm extends React.Component {

    constructor(props) {
        super(props);
        this.state = {data: props.data};
    }

    render() {
        const {booking, vehicleSelect, vehicleData, customer, drivers, fees, occasions} = this.props;

        return (
                <form className="grid-form">
                    <div className="row">
                        <div className="col">
                            <label>Is this a new or returning customer?</label>
                            <RadioMenu name="repeat_customer">
                                <RadioButton value="NEW">New Customer</RadioButton>
                                <RadioButton value="EXIST">Returning Customer</RadioButton>
                            </RadioMenu>
                        </div>
                    </div>

                    <div className="row new-customer-row">
                        <div className="col-1-2">
                            <label htmlFor="customer.first_name">First Name</label>
                            <Input id="customer.first_name" name="customer.first_name" type="text"/>
                        </div>
                        <div className="col-1-2">
                            <label htmlFor="customer.last_name">Last Name</label>
                            <Input id="customer.last_name" name="customer.last_name" type="text"/>
                        </div>
                    </div>
                    // .. more inputs ..

其中<RadioMenu>呈现的<RadioButton>列表又包含<Input>

<Input>看起来像这样:

export default function Input(attrs) {
    return <input {...attrs}/>;
}

我把它变成了一个React组件,希望我能用它做一些有用的东西。

基本上,我想要在输入更改后立即将所有表单数据填充到this.data.INPUT_NAME。如果输入名称包含.,那么我想将其放入子对象中。例如,customer.last_name将存储在this.state.data.customer.last_name中。我还想使用this.state.data为所有Input元素设置初始值,而不必向每个元素显式添加value属性;它应该只知道使用输入的名称从数据对象中提取什么值。

我不知道如何处理这个问题。我的第一个想法是,我不应该返回<form>而应将其放入变量中,然后对其进行预处理,将onChangevalue属性添加到Input类型的任何内容中但是,即使我尝试过,我也不认为它会对我的RadioMenu有效,因为RadioMenu不是Input类型,我认为我不能把它归还给孩子们。

我可以尝试使用此context功能但警告却吓跑了我。

我还没有看过Flux / Reflux / Redux / xyz,但我认为我真的不想在游戏初期加入另一个框架;我想了解如何妥善处理这个问题。

那么,如何将所有表单数据都放入this.state.data

收音机小部件看起来像这样。如有必要,我愿意改变它们。这是我的第一个自定义输入小部件。

// RadioMenu.jsx
import React from 'react';
import {cloneWithProps} from '../helpers/react-helpers';
import Input from './Input';

export default class RadioMenu extends Input {

    constructor(props) {
        super(props);
        this.state = {value: props.value};
    }

    onChange = ev => {
        this.setState({value: ev.target.value});
        if(this.props.onChange) {
            this.props.onChange(ev);
        }
    };

    render() {
        let {children, name, onChange, ...attrs} = this.props;
        return (
            <div className="radio-horizontal radio-menu" {...attrs}>
                {cloneWithProps(children, btn => ({
                    name,
                    checked: btn.props.value == this.state.value,
                    onChange: this.onChange
                }))}
            </div>
        );
    }
}

// RadioButton.jsx
export default function RadioButton({children, ...attrs}) {
    return (
        <label className="checkable">
            <input type="radio" {...attrs}/>
            <span>{children}</span>
        </label>
    );
}

我试图使用继承,所以我可以拔出所有Input s,无论他们是否自定义,但我似乎无法在React中使用它。对于子类,mycomp.type instanceof Input不会返回true。我知道React建议构图而不是继承,但我不知道如何做到这一点。

2 个答案:

答案 0 :(得分:1)

这种问题是我们拥有像Redux / Flux这样的库/模式的原因,但这并不意味着没有React就无法解决,只是稍微难一点。

在这种特定情况下,您有几个选择。

儿童家长活动

如果您将<RadioButton />组件更改为接受onChange处理程序,则可以侦听按钮的更改并将其直接置于您的状态。

function RadioButton(props) {
  return (
    // pass the onChange prop down
    <input type="radio" onChange={props.onChange} />
  );
}

然后更新您的<BookingForm />组件以使用这个新的处理程序道具。

const setRadioState = e => this.setState({ radio: e.target.value });
// ...
<RadioMenu name="repeat_customer">
  <RadioButton value="NEW" onChange={setRadioState}>New Customer</RadioButton>
  <RadioButton value="EXIST" onChange={setRadioState}>Returning Customer</RadioButton>
</RadioMenu>

访问表格

您可以收听submit事件的表单,然后遍历表单元素,以构建可以放入您所在州的对象。

render() {
  // ...
  <form onSubmit={this.onSubmit}>
  // ...
},
onSubmit(e) {
  const form = e.target;
  const elements = form.elements;
  // remove numeric keys
  const keys = Object.keys(elements).filter(k => /[^\d]/.test(k);
  const data = {};
  keys.forEach(k => data[k] = elements[k].value);
  this.setState(data);
}

如果您没有收听submit事件并希望按下按钮提交,那么您需要使用refs来获取该事件的实例形式。

我或多或少只是让这种方法脱颖而出,所以要警惕边缘情况。

答案 1 :(得分:0)

ReactLink会做你想做的事,但它正在走出困境。幸运的是,只需几行代码即可轻松重新创建此功能。

您可以使用此组件代替<input>

import React from 'react';

export default class LinkedStateInput extends React.Component {
    render() {
        const {value, ...attrs} = this.props;
        return <input {...attrs} value={value.value} onChange={ev => value.requestChange(ev.target.value)} />;
    }
}

用法示例:

<LinkedStateInput value={this.linkState('passenger_count')} type="text"/>

现在只需向BookingForm添加一个方法来处理状态更新:

linkState(name) {
    return {
        value: _.get(this.state.data, name, ''),
        requestChange: value => {
            let data = _.clone(this.state.data);
            _.set(data, name, value);
            this.setState({data})
        }
    }
}

我在这里使用lodash来处理深度集/获取。

RadioMenu变得更加简单,因为现在它甚至不必记住自己的状态:

export default function RadioMenu({children, name, valueLink}) {
    return (
        <div className="radio-horizontal radio-menu">
            {
                valueLink
                ? cloneWithProps(children, btn => ({
                    name,
                    checked: btn.props.value === valueLink.value,
                    onChange: ev => valueLink.requestChange(ev.target.value)
                }))
                : cloneWithProps(children, {name})
            }
        </div>
    );
}