我有一张表格,看起来像这样:
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>
而应将其放入变量中,然后对其进行预处理,将onChange
和value
属性添加到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建议构图而不是继承,但我不知道如何做到这一点。
答案 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>
);
}