REACT - 将事件处理程序附加到子级

Lor*_*nzo 6 javascript reactjs

我正在尝试在 React 中创建一个 Form 组件,有点像Formik,但更简单。

我的想法是onChange为所有孩子添加一个处理程序。我正在这样做children.map()。它有效,但是我收到一个关键警告

Warning: Each child in a list should have a unique "key" prop.
Run Code Online (Sandbox Code Playgroud)

我知道没有办法抑制这种情况,所以也许有更好的方法来创建这个表单组件?<input>另外,如果不是直系孩子,我应该如何处理这种情况?

编辑:我知道如何避免这个问题,我主要想要解决这个问题的最佳方法,包括嵌套输入的情况。

这是我想如何使用它:

<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>
Run Code Online (Sandbox Code Playgroud)

这是我的代码:

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;
Run Code Online (Sandbox Code Playgroud)

hel*_*joe 4

如果您使用渲染道具,则根本不会遇到unique "key"道具问题(这也是 Formik 的实现方式)。

\n\n

您的组件可以很容易地设置为handleChange作为渲染道具传递给其子组件,并且这也不需要您将其作为input直接子组件。

\n\n
class Form extends Component {\n  ...\n  handleInputChange() {...}\n\n  render() {\n    // Note that we call children as a function,\n    // passing `handleChangeInput` as the argument.\n    // If you want to pass other other things to the\n    // children (handleSubmit, values from state), just\n    // add them to the argument you\'re passing in.\n    this.props.children({this.handleInputChange});\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

使用方法如下:

\n\n
<Form>\n  // Notice that <Form> needs its immediate child to be\n  // a function, which has your handler as the argument:\n  {({handeInputChange}) => {\n    return (\n      <form>\n        <input type="text" name="owner" onChange={handleInputChange} />\n        <input type="checkbox" name="toggle" onChange={handleInputChange} />\n        <div>\n          // inputs can be nested in other elements\n          <input name=\xe2\x80\x9cinner\xe2\x80\x9d onChange={handleInputChange} />\n        <div>\n      <form>\n    )  \n  }}\n</Form>\n
Run Code Online (Sandbox Code Playgroud)\n\n

编辑:您在评论中提到您不想将处理程序显式传递给每个输入。实现此目的的另一种方法是使用 React Context,在表单中使用 Provider,并将每个 Providerinput包装在一个 Consumer 中:

\n\n
const FormContext = React.createContext();\n\nconst FormInput = (props) => {\n  const {handleInputChange} = useContext(FormContext);\n  return <input handleInputChange={handleInputChange} {...props} />\n}\n\nclass Form extends Component {\n  ...\n  handleInputChange() {...}\n\n  render() {\n    // Pass anything you want into `value` (state, other handlers),\n    // it will be accessible in the consumer \n    <Provider value={{ handleInputChange: this.handleInputChange }}>\n      <form>\n        {this.props.children}\n      </form>\n    </Provider>\n  }\n}\n\n// Usage:\n<Form>\n  <FormInput type="text" name="owner" />\n  <FormInput type="submit" name="submit" />\n  <div>\n      <FormInput type="checkbox" name="toggle" />\n  </div>\n</Form>\n
Run Code Online (Sandbox Code Playgroud)\n\n

事实上,Formik 也有这个选项,无论是Field组件还是connect功能。

\n