在 React 中,如何命名属于数组一部分的表单字段?
In React, how do I name a field of my form that is part of an array?
我正在构建 React 16.13.0 应用程序。在我的表单中,我想提交数据(一个地址)作为数组的一部分,所以我这样设置我的状态...
constructor(props) {
super(props);
this.state = {
countries: [],
provinces: [],
errors: [],
newCoop: {
name: '',
types: [],
addresses: [{
formatted: '',
locality: {
name: '',
postal_code: '',
state: ''
},
country: FormContainer.DEFAULT_COUNTRY,
}],
enabled: true,
email: '',
phone: '',
web_site: ''
},
然后我创建了这些函数来管理对输入字段的更改...
handleInput(e) {
let self=this
let value = e.target.value;
let name = e.target.name;
this.setValue(self.state.newCoop,name,value)
}
setValue = (obj,is, value) => {
if (typeof is == 'string')
return this.setValue(obj,is.split('.'), value);
else if (is.length === 1 && value!==undefined) {
return this.setState({obj: obj[is[0]] = value});
} else if (is.length === 0)
return obj;
else
return this.setValue(obj[is[0]],is.slice(1), value);
}
...
<Input inputType={'text'}
title= {'Street'}
name= {'addresses[0].formatted'}
value={this.state.newCoop.addresses[0].formatted}
placeholder = {'Enter address street'}
handleChange = {this.handleInput}
errors = {this.state.errors}
/> {/* Address street of the cooperative */}
Input.jsx 文件如下所示...
const Input = (props) => {
return (
<div className="form-group">
<FormLabel>{props.title}</FormLabel>
<FormControl
isInvalid={props.errors && Boolean(props.errors[props.name])}
type={props.type}
id={props.name}
name={props.name}
value={props.value}
placeholder={props.placeholder}
onChange={props.handleChange}
/>
{props.errors && props.errors[props.name] && (
<FormControl.Feedback type="invalid">
{props.errors[props.name].map((error, index) => (
<div key={`field-error-${props.name}-${index}`} className="fieldError">{error}</div>
))}
</FormControl.Feedback>
)}
</div>
)
}
export default Input;
但是,当我尝试更改值时,出现以下错误。我不确定我还需要做什么来命名我的组件,以便我可以成功地更改它的值。我宁愿不更改我的构造函数中的数据结构,但我愿意这样做。
TypeError: Cannot set property 'formatted' of undefined
FormContainer.setValue
src/containers/FormContainer.jsx:127
124 | if (typeof is == 'string')
125 | return this.setValue(obj,is.split('.'), value);
126 | else if (is.length === 1 && value!==undefined) {
> 127 | return this.setState({obj: obj[is[0]] = value});
| ^
128 | } else if (is.length === 0)
129 | return obj;
130 | else
你的代码很混乱,这是你问题的一部分,你的代码的另一个问题是在反应状态下嵌套对象不是好的做法。您可以了解更多 .
这是您可以使用代码设置状态的示例,但是请注意,这是解决问题的糟糕方法:
handleInput(e) {
let value = e.target.value;
this.setState(prevState =>{
...prevState,
newCoop: {
...prevState.newCoop
addresses: [
{
...prevState.newCoop[0].addresses
formatted: value
}
]
}
})
}
问题:
Cannot set property 'formatted' of undefined
// Reason : because you can't access obj["addresses[0]"]["formatted"]
// Solution : it should look something like obj["addresses"][0]["formatted"]
因为您按 .
拆分字符串,所以您得到的结果是
[
"addresses[0]",
"formatted"
]
现在你已经成功拆分了字符串,
您正在尝试通过名称获取对象,特别是 obj["addresses[0]"]
,但您无法像这样访问对象索引,
它会给你 undefined
,所以你会得到上面的错误。您可以通过代码段下面的 运行 检查确切的错误,
const obj = {
name: '',
types: [],
addresses: [{
formatted: '',
locality: {
name: '',
postal_code: '',
state: ''
},
}],
};
const names = "addresses[0].formatted".split(".")
console.log("obj['addresses[0]'] ===>" , obj[names[0]])
console.log("obj['addresses[0]']['formatted'] ===>" , obj[names[0]][names[1]])
解决方案:
所以现在的问题是,如果不是obj["addresses[0]"]
,那么解决方案是obj["addresses"]["0"]
,
所以你有 2 个选项 :
首先:把这个addresses[0].formatted
改成addresses.0.formatted
其次: 你需要用 .split(/[\[\].]+/)
分开刺
我更喜欢第二个选项,因为这个 addresses[0].formatted
看起来像真实的表单名称,它应该是这样的,您也可以在下面的代码片段中检查。
const obj = {
name: '',
types: [],
addresses: [{
formatted: '',
locality: {
name: '',
postal_code: '',
state: ''
},
}],
};
const names = "addresses[0].formatted".split(/[\[\].]+/)
console.log("obj['addresses'] ==>" , obj[names[0]])
console.log("obj['addresses']['0'] ==>" , obj[names[0]][names[1]])
console.log("obj['addresses']['0']['formatted'] ==>" , obj[names[0]][names[1]][names[2]])
注意:
现在,一旦你解决了这个问题,真正的问题就出现了,obj: obj[is[0]] = value
,这里 obj
是对象所以这会抛出错误,还有你的 setValue
函数仅限于该功能,它应该是通用的
handleInput = e => {
let name = e.target.name;
let value = e.target.value;
const keys = name.split(/[\[\].]+/);
this.setState(this.updateValue(this.state, keys, value));
};
// I've created a recursive function such that it will create a
// copy of nested object so that it won't mutate state directly
// obj : your state
// name : input name
// value : value that you want to update
updateValue = (obj, name, value, index = 0) => {
if (name.length - 1 > index) {
const isArray = Array.isArray(obj[name[index]]);
obj[name[index]] = this.updateValue(
isArray ? [...obj[name[index]]] : { ...obj[name[index]] },
name,
value,
index + 1
);
} else {
obj = { ...obj, [name[index]]: value };
}
return obj;
};
工作演示:
我正在构建 React 16.13.0 应用程序。在我的表单中,我想提交数据(一个地址)作为数组的一部分,所以我这样设置我的状态...
constructor(props) {
super(props);
this.state = {
countries: [],
provinces: [],
errors: [],
newCoop: {
name: '',
types: [],
addresses: [{
formatted: '',
locality: {
name: '',
postal_code: '',
state: ''
},
country: FormContainer.DEFAULT_COUNTRY,
}],
enabled: true,
email: '',
phone: '',
web_site: ''
},
然后我创建了这些函数来管理对输入字段的更改...
handleInput(e) {
let self=this
let value = e.target.value;
let name = e.target.name;
this.setValue(self.state.newCoop,name,value)
}
setValue = (obj,is, value) => {
if (typeof is == 'string')
return this.setValue(obj,is.split('.'), value);
else if (is.length === 1 && value!==undefined) {
return this.setState({obj: obj[is[0]] = value});
} else if (is.length === 0)
return obj;
else
return this.setValue(obj[is[0]],is.slice(1), value);
}
...
<Input inputType={'text'}
title= {'Street'}
name= {'addresses[0].formatted'}
value={this.state.newCoop.addresses[0].formatted}
placeholder = {'Enter address street'}
handleChange = {this.handleInput}
errors = {this.state.errors}
/> {/* Address street of the cooperative */}
Input.jsx 文件如下所示...
const Input = (props) => {
return (
<div className="form-group">
<FormLabel>{props.title}</FormLabel>
<FormControl
isInvalid={props.errors && Boolean(props.errors[props.name])}
type={props.type}
id={props.name}
name={props.name}
value={props.value}
placeholder={props.placeholder}
onChange={props.handleChange}
/>
{props.errors && props.errors[props.name] && (
<FormControl.Feedback type="invalid">
{props.errors[props.name].map((error, index) => (
<div key={`field-error-${props.name}-${index}`} className="fieldError">{error}</div>
))}
</FormControl.Feedback>
)}
</div>
)
}
export default Input;
但是,当我尝试更改值时,出现以下错误。我不确定我还需要做什么来命名我的组件,以便我可以成功地更改它的值。我宁愿不更改我的构造函数中的数据结构,但我愿意这样做。
TypeError: Cannot set property 'formatted' of undefined
FormContainer.setValue
src/containers/FormContainer.jsx:127
124 | if (typeof is == 'string')
125 | return this.setValue(obj,is.split('.'), value);
126 | else if (is.length === 1 && value!==undefined) {
> 127 | return this.setState({obj: obj[is[0]] = value});
| ^
128 | } else if (is.length === 0)
129 | return obj;
130 | else
你的代码很混乱,这是你问题的一部分,你的代码的另一个问题是在反应状态下嵌套对象不是好的做法。您可以了解更多
这是您可以使用代码设置状态的示例,但是请注意,这是解决问题的糟糕方法:
handleInput(e) {
let value = e.target.value;
this.setState(prevState =>{
...prevState,
newCoop: {
...prevState.newCoop
addresses: [
{
...prevState.newCoop[0].addresses
formatted: value
}
]
}
})
}
问题:
Cannot set property 'formatted' of undefined
// Reason : because you can't access obj["addresses[0]"]["formatted"]
// Solution : it should look something like obj["addresses"][0]["formatted"]
因为您按 .
拆分字符串,所以您得到的结果是
[
"addresses[0]",
"formatted"
]
现在你已经成功拆分了字符串,
您正在尝试通过名称获取对象,特别是 obj["addresses[0]"]
,但您无法像这样访问对象索引,
它会给你 undefined
,所以你会得到上面的错误。您可以通过代码段下面的 运行 检查确切的错误,
const obj = {
name: '',
types: [],
addresses: [{
formatted: '',
locality: {
name: '',
postal_code: '',
state: ''
},
}],
};
const names = "addresses[0].formatted".split(".")
console.log("obj['addresses[0]'] ===>" , obj[names[0]])
console.log("obj['addresses[0]']['formatted'] ===>" , obj[names[0]][names[1]])
解决方案:
所以现在的问题是,如果不是obj["addresses[0]"]
,那么解决方案是obj["addresses"]["0"]
,
所以你有 2 个选项 :
首先:把这个addresses[0].formatted
改成addresses.0.formatted
其次: 你需要用 .split(/[\[\].]+/)
我更喜欢第二个选项,因为这个 addresses[0].formatted
看起来像真实的表单名称,它应该是这样的,您也可以在下面的代码片段中检查。
const obj = {
name: '',
types: [],
addresses: [{
formatted: '',
locality: {
name: '',
postal_code: '',
state: ''
},
}],
};
const names = "addresses[0].formatted".split(/[\[\].]+/)
console.log("obj['addresses'] ==>" , obj[names[0]])
console.log("obj['addresses']['0'] ==>" , obj[names[0]][names[1]])
console.log("obj['addresses']['0']['formatted'] ==>" , obj[names[0]][names[1]][names[2]])
注意:
现在,一旦你解决了这个问题,真正的问题就出现了,obj: obj[is[0]] = value
,这里 obj
是对象所以这会抛出错误,还有你的 setValue
函数仅限于该功能,它应该是通用的
handleInput = e => {
let name = e.target.name;
let value = e.target.value;
const keys = name.split(/[\[\].]+/);
this.setState(this.updateValue(this.state, keys, value));
};
// I've created a recursive function such that it will create a
// copy of nested object so that it won't mutate state directly
// obj : your state
// name : input name
// value : value that you want to update
updateValue = (obj, name, value, index = 0) => {
if (name.length - 1 > index) {
const isArray = Array.isArray(obj[name[index]]);
obj[name[index]] = this.updateValue(
isArray ? [...obj[name[index]]] : { ...obj[name[index]] },
name,
value,
index + 1
);
} else {
obj = { ...obj, [name[index]]: value };
}
return obj;
};
工作演示: