我正在尝试创建一个React形式的表单组件,有点像Formik,但更简单。
我的思维方式涉及向所有孩子添加onChange
处理程序。我正在使用children.map()
进行此操作。可以,但是我得到一个关键警告
Warning: Each child in a list should have a unique "key" prop.
我知道没有办法抑制这种情况,所以也许有更好的方法来创建此Form组件?
另外,当<input>
不是直系子女时,该如何处理?
编辑:我知道如何避免该问题,我主要是寻求解决此问题的最佳方法,包括嵌套输入的情况。
这是我要使用的方式:
<Form>
<label htmlFor="owner">Owner</label>
<input
type="text"
name="owner"
<label htmlFor="description">Description</label>
<input
type="text"
name="description"
<input
type="submit"
value="Submit" />
</Form>
这是我的代码:
import React from 'react';
import PropTypes from 'prop-types';
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {}
this.handleInputChange = this.handleInputChange.bind(this);
// this.handleSubmit = this.handleSubmit.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value =
target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
console.log(`${name} : ${value}`)
this.setState({
[name]: value
});
}
render() {
return (
<form>
{this.props.children.map((child) => {
if (child.type === "input") {
return (
<input
onChange={this.handleInputChange}
{...child.props} />
)
}
})}
</form>
)
}
}
export default Form;
答案 0 :(得分:2)
如果使用渲染道具,则根本不会遇到
address <- list("1 Main Street", "2 Hope Street")
lapply(address, py$print_address)
道具问题(这也是Formik的实现方式)。
您的组件很容易设置为将unique "key"
传递给它的子代作为渲染道具,这也不需要您将handleChange
作为直接子代。
input
使用方法如下:
class Form extends Component {
...
handleInputChange() {...}
render() {
// Note that we call children as a function,
// passing `handleChangeInput` as the argument.
// If you want to pass other other things to the
// children (handleSubmit, values from state), just
// add them to the argument you're passing in.
this.props.children({this.handleInputChange});
}
}
编辑:您在评论中提到,您不想将处理程序显式传递给每个输入。实现此目标的另一种方法是使用React Context,在您的Form中提供一个Provider,并将每个<Form>
// Notice that <Form> needs its immediate child to be
// a function, which has your handler as the argument:
{({handeInputChange}) => {
return (
<form>
<input type="text" name="owner" onChange={handleInputChange} />
<input type="checkbox" name="toggle" onChange={handleInputChange} />
<div>
// inputs can be nested in other elements
<input name=“inner” onChange={handleInputChange} />
<div>
<form>
)
}}
</Form>
包装在使用者中:
input
事实上,Formik也具有此选项,可以使用const FormContext = React.createContext();
const FormInput = (props) => {
const {handleInputChange} = useContext(FormContext);
return <input handleInputChange={handleInputChange} {...props} />
}
class Form extends Component {
...
handleInputChange() {...}
render() {
// Pass anything you want into `value` (state, other handlers),
// it will be accessible in the consumer
<Provider value={{ handleInputChange: this.handleInputChange }}>
<form>
{this.props.children}
</form>
</Provider>
}
}
// Usage:
<Form>
<FormInput type="text" name="owner" />
<FormInput type="submit" name="submit" />
<div>
<FormInput type="checkbox" name="toggle" />
</div>
</Form>
组件或Field
函数。
答案 1 :(得分:1)
我认为这是您需要的,已经可以将子索引添加为 key 了,因为顺序不会改变,并且在这种情况下,reduce不会在数组中返回null,以防未输入child,map + filter也可以解决该问题:
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.handleInputChange = this.handleInputChange.bind(this);
// this.handleSubmit = this.handleSubmit.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const name = target.name;
console.log(`${name} : ${value}`);
this.setState({
[name]: value
});
}
render() {
return (
<form>
{this.props.children.reduce((childrenAcc, child, index) => {
if (child.type === "input") {
return [
...childrenAcc,
<input
key={index}
onChange={this.handleInputChange}
{...child.props}
/>
];
}
return childrenAcc;
}, [])}
</form>
);
}
}
function App() {
return (
<Form>
<label htmlFor="owner">Owner</label>
<input type="text" name="owner" />
<label htmlFor="description">Description</label>
<input type="text" name="description" />
<input type="submit" value="Submit" />
</Form>
);
}
选中此sandbox。