admin管理员组文章数量:1401624
I'm trying to create a Form ponent in react, kind of like Formik, but way simpler.
The way I'm thinking involves adding a onChange
handler to all the children. I am doing this with children.map()
. It works, however I get a key warning
Warning: Each child in a list should have a unique "key" prop.
I know there's no way to suppress this, so maybe there's a better approach to create this Form ponent?
Also, how should I approach the case when the <input>
is not a direct child?
Edit: I know how to avoid the problem, I mainly want the best way to approach this, including cases of nested inputs.
Here is how I want to use it:
<Form>
<label htmlFor="owner">Owner</label>
<input
type="text"
name="owner"
id="owner"
/>
<label htmlFor="description">Description</label>
<input
type="text"
name="description"
id="description"
/>
<input
type="submit"
value="Submit"
/>
</Form>
and here is my code:
import React from 'react';
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {}
this.handleInputChange = this.handleInputChange.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;
I'm trying to create a Form ponent in react, kind of like Formik, but way simpler.
The way I'm thinking involves adding a onChange
handler to all the children. I am doing this with children.map()
. It works, however I get a key warning
Warning: Each child in a list should have a unique "key" prop.
I know there's no way to suppress this, so maybe there's a better approach to create this Form ponent?
Also, how should I approach the case when the <input>
is not a direct child?
Edit: I know how to avoid the problem, I mainly want the best way to approach this, including cases of nested inputs.
Here is how I want to use it:
<Form>
<label htmlFor="owner">Owner</label>
<input
type="text"
name="owner"
id="owner"
/>
<label htmlFor="description">Description</label>
<input
type="text"
name="description"
id="description"
/>
<input
type="submit"
value="Submit"
/>
</Form>
and here is my code:
import React from 'react';
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {}
this.handleInputChange = this.handleInputChange.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;
Share
Improve this question
edited Feb 5, 2022 at 17:31
Ledorub
4665 silver badges10 bronze badges
asked Jun 15, 2019 at 13:56
LorenzoLorenzo
2,21013 silver badges29 bronze badges
3
- You might want to consider using context instead, if you are worried about inputs not being direct children. You cannot reliably traverse further down than children from a ponent, but you can inject context at any level. – Wolfie Commented Jun 15, 2019 at 14:21
- Possible duplicate of Understanding unique keys for array children in React.js – Anurag Srivastava Commented Jun 15, 2019 at 14:22
- @AnuragSrivastava I do indeed know how to avoid the warning, I'm asking for a better way to actually eliminate the problem, plus also dealing with nested inputs – Lorenzo Commented Jun 15, 2019 at 18:23
2 Answers
Reset to default 4If you use a render prop, you won't run into the unique "key"
prop issue at all (This is also how Formik is implemented).
Your ponent would be easy to set up to pass handleChange
to its children as a render prop, and this would also not require you to have input
as a direct child.
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});
}
}
Here's how you use it:
<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>
EDIT: You mentioned in a ment that you didn't want to explicitly pass the handler to each of your inputs. Another way to achieve this is with React Context, with a Provider in your Form, and each input
wrapped in a consumer:
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>
In fact, Formik has this option as well, with either the Field
ponent, or the connect
function.
i think this is what you need, already you could add child index as key since there order won't change, and reduce here is not returning null in the array in case the type of the child is not input, map+filter could resolve it also:
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>
);
}
check this sandbox .
本文标签: javascriptREACTAttach event handler to childrenStack Overflow
版权声明:本文标题:javascript - REACT - Attach event handler to children - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744276844a2598454.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论