我正在尝试在我的应用程序中创建一个表单。我创建了一个input.js组件,该组件导入了我的contact.js组件,然后进行了映射。我得到一个“警告:数组或迭代器中的每个子代都应具有唯一的“键”属性。请在控制台中检查input
的呈现方法,但我不明白为什么,因为每个输入组件已设置了唯一键。当我在chrome inspector的react选项卡中检查它们时,它们都有唯一的密钥集。
这是我的contact.js组件:
import React, { Component } from 'react';
import Input from './Input/input';
import Button from './Button/Button';
import Spinner from '../UI/Spinner/Spinner';
import {checkValidity} from '../../shared/utility';
import axios from '../../axios-contact';
class ContactForm extends Component {
state = {
contactForm: {
name: {
elementType: 'input',
elementConfig: {
inputprops: {
type: 'text',
id: 'name',
name: 'name',
required: 'required'
} ,
label: 'Name',
htmlFor: 'name',
invalid: 'Please enter your firstname',
value: '',
},
validation: {
required: true,
minLength: 2,
maxLength: 30
},
valid: false,
touched: false
},
company: {
elementType: 'input',
elementConfig: {
inputprops: {
type: 'text',
id: 'company',
name: 'company',
required: 'required'
},
label: 'Company',
htmlFor: 'company',
invalid: 'Please enter your company name',
value: '',
},
validation: {
required: true,
minLength: 2,
maxLength: 30
},
valid: false,
touched: false
},
location: {
elementType: 'input',
elementConfig: {
inputprops: {
type: 'text',
id: 'location',
name: 'location',
required: 'required'
},
label: 'Company location (city / country)',
htmlFor: 'location',
invalid: 'Please enter your location',
value: '',
},
validation: {
required: true,
minLength: 2,
maxLength: 30
},
valid: false,
touched: false
},
email: {
elementType: 'email',
elementConfig: {
inputprops: {
type: 'email',
id: 'email',
name: 'email',
required: 'required'
},
label: 'Email',
htmlFor: 'email',
invalid: 'Please enter a propper email address',
value: '',
},
validation: {
required: true,
isEmail: true,
minLength: 7,
maxLength: 40
},
valid: false,
touched: false
},
phone: {
elementType: 'input',
elementConfig: {
inputprops: {
type: 'tel',
id: 'phone',
name: 'phone',
required: false
},
label: 'Phone',
htmlFor: 'phone',
invalid: 'Please enter a propper phone number',
value: '',
},
validation: {
required: false,
minLength: 6,
maxLength: 30
},
valid: true,
touched: false
},
message: {
elementType: 'textarea',
elementConfig: {
inputprops: {
type: 'textarea',
id: 'message',
name: 'message',
required: 'required',
rows: 4
},
label: 'Message',
htmlFor: 'message',
invalid: 'Please enter a message',
value: '',
},
validation: {
required: true,
minLength: 2,
maxLength: 500
},
valid: false,
touched: false
},
compliance: {
elementType: 'checkbox',
containerClass: 'custom-control custom-checkbox',
inputClass: 'custom-control-input',
elementConfig: {
inputprops: {
type: 'checkbox',
id: 'gdpr',
name: 'gdpr',
required: 'required'
},
label: 'I consent to having this website store my submitted information so they can respond to my inquiry.',
htmlFor: 'gdpr',
invalid: 'Please give your consent before proceeding',
value: '',
},
validation: {
required: true,
isCheckbox: true,
isToggled: false
},
valid: false,
touched: false
}
},
formIsValid: false,
loading: false,
sent: false
}
contactHandler = ( event ) => {
event.preventDefault();
this.setState( { loading: true } );
const formData = {}
for (let formElementIdentifier in this.state.contactForm) {
formData[formElementIdentifier] = this.state.contactForm[formElementIdentifier].elementConfig.value;
}
axios.post('/contacts.json', formData)
.then(response => {
this.setState({ loading: false, sent: true });
console.log(formData);
})
.catch(error => {
this.setState({ loading: false, sent: true });
console.log(formData);
});
}
inputChangedHandler = (event, inputIdentifier) => {
const updatedContactForm = {
...this.state.contactForm
};
const updatedFormElement = {
...updatedContactForm[inputIdentifier]
};
updatedFormElement.elementConfig.value = event.target.value;
updatedFormElement.valid = checkValidity(updatedFormElement.elementConfig.value, updatedFormElement.validation);
updatedFormElement.touched = true;
updatedFormElement.validation.isToggled = !updatedFormElement.validation.isToggled;
updatedContactForm[inputIdentifier] = updatedFormElement;
let formIsValid = true;
for ( let inputIdentifier in updatedContactForm) {
formIsValid = updatedContactForm[inputIdentifier].valid && formIsValid;
}
this.setState({contactForm: updatedContactForm, formIsValid: formIsValid});
}
render () {
const formElementsArray = [];
for (let key in this.state.contactForm) {
formElementsArray.push({
id: key,
config: this.state.contactForm[key]
});
}
let form = (
<form onSubmit={this.contactHandler} name="contact">
{formElementsArray.map(formElement =>(
<Input
key={formElement.id}
elementType={formElement.config.elementType}
containerClass={formElement.config.containerClass}
inputClass={formElement.config.inputClass}
elementConfig={formElement.config.elementConfig}
value={formElement.config.value}
invalid={!formElement.config.valid}
shoudValidate={formElement.config.validation}
touched={formElement.config.touched}
checked={formElement.config.validation.isToggled}
changed={(event) => this.inputChangedHandler(event, formElement.id)}
exited={(event) => this.inputChangedHandler(event, formElement.id)} />
))}
<Button disabled={!this.state.formIsValid} />
</form>
);
if (this.state.loading) {
form = <Spinner />
}
if (this.state.sent) {
form = <p id="contact-message" className="contact-message">Thank you for your message.<br /> We will respond as soon as possible.</p>
}
return (
<div className="contact">
<section id="contact-form" className="contact-form">
<h1>Contact</h1>
{form}
</section>
</div>
)
}
};
export default ContactForm;
这是我的input.js组件:
import React from 'react';
import { NavLink } from 'react-router-dom';
const input = ( props ) => {
let label = <label htmlFor={props.elementConfig.htmlFor}>{props.elementConfig.label}</label>;
let inputElement = null;
let errorlabel = null;
let inputClass = ['input'];
const errorid = [props.elementConfig.id];
if(props.invalid && props.shoudValidate && props.touched) {
inputClass.push('error');
}
switch (props.elementType) {
case ('input'):
inputElement = <input
className ={inputClass.join(' ')}
{...props.elementConfig.inputprops}
value={props.elementConfig.value}
onChange={props.changed}
onBlur={props.exited} />;
break;
case ('email'):
inputElement = <input
className ={inputClass.join(' ')}
{...props.elementConfig.inputprops}
value={props.elementConfig.value}
onChange={props.changed}
onBlur={props.exited} />;
break;
case ( 'textarea' ):
inputElement = <textarea
className ={inputClass.join(' ')}
{...props.elementConfig.inputprops}
value={props.elementConfig.value}
onChange={props.changed}
onBlur={props.exited} />;
break;
case ( 'checkbox' ):
inputElement = <input
className ={[props.inputClass, inputClass.join(' ')].join(' ')}
{...props.elementConfig.inputprops}
value={!props.checked}
onChange={props.changed} />;
label = <label htmlFor={props.elementConfig.htmlFor} className="custom-control-label">This form collects your name, e-mail, phone number, company name, and location so that we may correspond with you. Read our <NavLink to="/privacy" exact>privacy policy</NavLink> for more information. By submitting the form, you consent to have StackApp collect the listed information.</label>;
break;
default:
inputElement = <input
className ={inputClass.join(' ')}
{...props.elementConfig.inputprops}
value={props.elementConfig.value}
onChange={props.changed}
onBlur={props.exited} />;
}
if(props.invalid && props.touched) {
errorlabel = <label id={errorid.join('-error')} className="error" htmlFor={props.elementConfig.htmlFor}>{props.elementConfig.invalid}</label>
};
let output = null;
if(props.elementType === 'checkbox') {
output = [inputElement, label, errorlabel];
} else {
output = [label, inputElement, errorlabel];
}
return (
<div role="group" className={props.containerClass}>
{output}
</div>
)
};
export default input;
我在这里想念什么?
答案 0 :(得分:3)
即使formElementsArray.map
似乎是最有可能的候选者,但在这种情况下,它并不是警告的来源。就像您在评论中提到的那样,每个键在构造上都是唯一的。错误来自input.js,您在其中分配output = [inputElement, label, errorlabel]
然后直接渲染{output}
。 React认为这是一个数组,但不知道它的大小是固定的,因此期望数组中的每个元素都具有唯一的key
属性。如果您在key
,inputElement
和label
上放置errorLabel
道具,警告应该消失。