简体   繁体   English

如何在 ReactJS 中实现带有受控组件的动态表单?

[英]How to implement a dynamic form with controlled components in ReactJS?

As I am looking at the examples in the reference for controlled form components<\/code> in react.js official website<\/a> , I am wondering how is one supposed to implement a form<\/code> in which you would be able to remove<\/code> and add<\/code> input<\/code> elements dynamically in such a way that they are controlled components?当我在 react.js 官方网站中<\/a>查看controlled form components<\/code>参考中的示例时,我想知道如何实现一个form<\/code> ,您可以在其中动态remove<\/code>和add<\/code> input<\/code>元素,这样的方式它们是受控组件? Is this even possible?这甚至可能吗?

In the examples we can see:在示例中我们可以看到:

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

How adding/removing input elements dynamically possible?如何动态添加/删除输入元素?

Yes, it is possible, you can add/remove input elements dynamically , But for that you need to take care of few things:是的,有可能,您可以dynamically添加/删除input元素,但为此您需要注意以下几点:

1- Proper binding of events. 1-正确绑定事件。

2- Array to store the values of each input element separately. 2-数组分别存储每个输入元素的值。

3- When user fill value in any input element then updating only that specific value in state. 3-当用户在任何输入元素中填充值时,仅更新状态中的该特定值。

Logic:逻辑:

Maintain an array inside state, that will store the values.在状态内部维护一个数组,该数组将存储值。 Use #array.map to create ui (input element) for each array values.使用#array.map为每个数组值创建 ui(输入元素)。 while creating the fields, use a remove button with each field, and pass the index of field in that function , it will help you to identify which field you wants to delete, do the same thing for onChange also.在创建字段时,对每个字段使用删除button ,并在该function传递字段的索引,这将帮助您确定要删除的字段,对onChange也做同样的事情。

Check this example:检查这个例子:

 class App extends React.Component { constructor(props) { super(props); this.state = { values: [] }; this.handleSubmit = this.handleSubmit.bind(this); } createUI(){ return this.state.values.map((el, i) => <div key={i}> <input type="text" value={el||''} onChange={this.handleChange.bind(this, i)} /> <input type='button' value='remove' onClick={this.removeClick.bind(this, i)}/> </div> ) } handleChange(i, event) { let values = [...this.state.values]; values[i] = event.target.value; this.setState({ values }); } addClick(){ this.setState(prevState => ({ values: [...prevState.values, '']})) } removeClick(i){ let values = [...this.state.values]; values.splice(i,1); this.setState({ values }); } handleSubmit(event) { alert('A name was submitted: ' + this.state.values.join(', ')); event.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit}> {this.createUI()} <input type='button' value='add more' onClick={this.addClick.bind(this)}/> <input type="submit" value="Submit" /> </form> ); } } ReactDOM.render(<App />, document.getElementById('container'));
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id='container'/>

Check the working jsfiddle : https://jsfiddle.net/mayankshukla5031/ezdxg224/检查工作jsfiddlehttps : jsfiddle

How adding/removing input elements dynamically with functional components?如何使用功能组件动态添加/删除输入元素?

The same component from the selected answer reviewed and rewrited as a functional component.所选答案中的相同组件被审查并重写为功能组件。

import React from 'react';
import { useState } from 'react';

function DynamicInput() {

    const [values, setValues] = useState({ val: []});

      function createInputs() {
        return values.val.map((el, i) =>
          <div key={i}>
            <input type="text" value={el||''} onChange={handleChange.bind(i)} />
            <input type='button' value='remove' onClick={removeClick.bind(i)} />
          </div>
        );
      }

    function handleChange(event) {
      let vals = [...values.val];
      vals[this] = event.target.value;
      setValues({ val: vals });
    }

    const addClick = () => {
      setValues({ val: [...values.val, '']})
    }

    const removeClick = () => {
      let vals = [...values.val];
      vals.splice(this,1);
      setValues({ val: vals });
    }

    const handleSubmit = event => {
      alert('A name was submitted: ' + values.val.join(', '));
      event.preventDefault();
    }

    return (
      <form onSubmit={handleSubmit}>
          {createInputs()}
          <input type='button' value='add more' onClick={addClick} />
          <input type="submit" value="Submit" />
      </form>
    );

}

export default DynamicInput;

If you want to use an object because you have many input field you can do this :如果你想使用一个对象,因为你有很多输入字段,你可以这样做

 class App extends React.Component { constructor(props) { super(props); this.state = { values: [] }; this.handleSubmit = this.handleSubmit.bind(this); }

 class App extends React.Component { constructor(props) { super(props); this.state = { values: [{question: '',response: ''}] }; this.handleSubmit = this.handleSubmit.bind(this); } createUI(){ return this.state.values.map((el, i) => <div key={i}> <input type="text" value={el.question} onChange={this.handleChange.bind(this, i)} /> <textarea type="text" value={el.response} onChange={this.handleChange.bind(this, i)} /> <input type='button' value='remove' onClick={this.removeClick.bind(this, i)}/> </div> ) } handleChange (i,event) { let values = [...this.state.values]; values[i][event.target.id] = event.target.value; this.setState({ values }); } addClick(){ this.setState(prevState => ({ values: [...prevState.values, {question: '',response: '',}]})) } removeClick(i){ let values = [...this.state.values]; values.splice(i,1); this.setState({ values }); } handleSubmit(event) { event.preventDefault(); alert(this.state.values.map(objet=> "{"+ " " + "'question' :" + " " + objet.question + " " + "'response' :" + " " + objet.response +" " + "}") ); } render() { return ( <form onSubmit={this.handleSubmit}> {this.createUI()} <input type='button' value='add more' onClick={this.addClick.bind(this)}/> <input type="submit" value="Submit" /> </form> ); } } ReactDOM.render(<App />, document.getElementById('container'));
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <!-- begin snippet: js hide: false console: true babel: false -->

              <input type="text"  value={el.response} onChange={this.handleChange.bind(this, i)}  id="response" a />
          
         <input type='button' value='remove' onClick={this.removeClick.bind(this, i)}/>
        
         
       </div>
      
       )
    }
  
    handleChange (i,event) {
       let values = [...this.state.values];
       values[i][event.target.id] =   event.target.value;
       this.setState({ values }); 
    }
    
    addClick(){
      this.setState(prevState => ({ values: [...prevState.values, {question: '',response: '',}]}))
    }
    
    removeClick(i){
       let values = [...this.state.values];
       values.splice(i,1);
       this.setState({ values });
    }
  
    handleSubmit(event) {
      event.preventDefault();
      alert(this.state.values.map(objet=> "{"+ "  "  + "'question' :" + "  " + objet.question + "  " + "'response' :" + "  " + objet.response +"  " + "}") );
    }
    render() {
      return (
        <form onSubmit={this.handleSubmit}>
            {this.createUI()}   
            <div  className="d-flex justify-content-end">
              <KamaminiButton onClick={this.addClick.bind(this)}  text='Ajouter une autre question' />
            </div>     
            <input type="submit" value="Submit" />
        </form>
      );
    }
  }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='container'/>

You can easily use your state or the props that you are passing from other components to decide what your form should be.您可以轻松地使用您从其他组件传递的状态或道具来决定您的表单应该是什么。

Here a dumb example:这是一个愚蠢的例子:

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    let myForm;
    if(this.props.someprop == true){
       myForm = (
           <form onSubmit={this.handleSubmit}>
              <label>
                 Name:
                 <input type="text" value={this.state.value} onChange=      {this.handleChange} />
              </label>
              <input type="submit" value="Submit" />
           </form>
       );
    }
    else if(this.state.statevar == "expectedValue"){
        myForm = (
           // other HTML with embedded JS here
        );
    }


    return (
         {myForm}
    );
  }
}

I did it in the render method just to be clear but all the logic can be moved in auxiliary functions.我在渲染方法中这样做只是为了清楚,但所有逻辑都可以在辅助函数中移动。

import React, { Component, } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
var childJson = []
export default class AddInvoice extends Component {
constructor(props) {
    super(props);
    this.state = {
        Json: [],
        rows: [{}]
    }
}

handleChange = idx => e => {
    const { name, value } = e.target;
    const rows = [...this.state.rows];
    rows[idx] = { [name]: value };
    this.setState({ rows });

    console.log(`rows-->>${this.state.rows[idx].amount}`);
    childJson.push(this.dynamicJson(this.state.rows, idx))
    this.setState({ Json: childJson })

};
handleAddRow = () => {
    const item = {
        name: "",
        mobile: "",
        btn: ""
    };
    this.setState({
        rows: [...this.state.rows, item]
    });
};
handleRemoveRow = (idx) => {
    this.state.rows.splice(idx, 1);
    this.setState({ rows: this.state.rows });
};
dynamicJson(rows, index) {
    return {
        "service": rows[index].text,
        "tax": rows[index].tax,
        "amount": rows[index].amount
    }
};
render() {
    return (
        <div className="ui one column grid">
            <div className=" row">
                <div className="one wide computer one wide tablet one wide mobile column">
                </div>
                <div className="fourteen wide computer fourteen wide tablet fourteen wide mobile column">
                    <h1 id="title_header">INVOICE-ADD NEW</h1>
                </div>
                <div className="one wide computer one wide tablet one wide mobile column">
                </div>
            </div>
            <div className=" row">
                <div className="one wide computer one wide tablet one wide mobile column">
                </div>
                <div className="fourteen wide computer fourteen wide tablet fourteen wide mobile column">
                    <div id="#cus_segment" className="ui segment" style={{ backgroundColor: '#f5f5f5' }}>
                        <div className="ui form">
                            <div className="ui  stackable grid">
                                <div className="six wide column">
                                    <div className="field">
                                        <label>Invoice No</label>
                                        <input type="text" name="invoiceno" placeholder="Invoice No" value={this.state.invoiceno} onChange={e => this.setState({ invoiceno: e.target.value })} />
                                    </div>
                                </div>
                                <div className=" six wide column">
                                    <div className="field">
                                        <label>Customer</label>
                                        <select className="ui fluid selection search dropdown" name="customer" value={this.state.customer} onChange={e => this.setState({ customer: e.target.value })}>
                                            <option defaultValue="">select</option>
                                            <option value="sohanpatil@gmail.com">sohanpatil@gmail.com</option>
                                            <option value="mayurawati@gmail.co">mayurawati@gmail.com</option>
                                            <option value="nasirpatel@gmail.com">nasirpatel@gmail.com</option>
                                            <option value="nandkishorshinde@gmail.com">nandkishorshinde@gmail.com</option>
                                        </select>
                                    </div>
                                </div>
                                <div className="one row">
                                    <div className="six wide column">
                                        <div className="field">
                                            <label>Invoice Date</label>
                                            <div className="ui calendar" id="calender1">
                                                <div className="ui input right icon">
                                                    <i className="calendar icon"></i>
                                                    <input type="text" placeholder="Invoice Date" value={this.state.invoicedate} onBlur={e => this.setState({ invoicedate: e.target.value })} />
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                    <div className="six wide column">
                                        <div className="field">
                                            <label>Due Date</label>
                                            <div className="ui calendar" id="calender2">
                                                <div className="ui input right icon">
                                                    <i className="calendar icon"></i>
                                                    <input type="text" placeholder="Due Date" value={this.state.duedate} onBlur={e => this.setState({ duedate: e.target.value })} />
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div className="two row">
                                    <div className="six wide column">
                                        <div className="field">
                                            <label>Header</label>
                                            <input type="text" name="header" placeholder="Header" value={this.state.header} onChange={e => this.setState({ header: e.target.value })} />
                                        </div>
                                    </div>
                                    <div className="six wide column">
                                        <div className="field">
                                            <label>Remark</label>
                                            <input type="text" name="remark" placeholder="Remark" value={this.state.remark} onChange={e => this.setState({ remark: e.target.value })} />
                                        </div>
                                    </div>
                                </div>
                                <div className="three row">
                                    <div className="ten wide column">
                                        <button className="ui primary button" type="submit">Save</button>
                                        <button className="ui  button" type="submit">Clear</button>
                                        <button className="ui  button" type="submit">Cancel</button>
                                    </div>
                                </div>

                                <div className="foure row">
                                    <div className="one wide column">
                                        <div className="field">
                                            <h4 style={{ textAlign: "center", borderRadius: 2 }}>Action</h4>
                                        </div>
                                    </div>
                                    <div className="two wide column" style={{ marginLeft: 55 }}>
                                        <div className="field">
                                            <h4 style={{ textAlign: "center", borderRadius: 2 }}>Text</h4>
                                        </div>
                                    </div>
                                    <div className="three column" style={{ marginLeft: 200 }}>
                                        <div className="field">
                                            <h4 style={{ textAlign: "center", borderRadius: 2 }}>Tax</h4>
                                        </div>
                                    </div>
                                    <div className="foure wide column" style={{ marginLeft: 190 }}>
                                        <div className="field">
                                            <h4 style={{ textAlign: "center", borderRadius: 2 }}>Amount</h4>
                                        </div>
                                    </div>
                                </div>
                                {this.state.rows.map((item, idx) => (
                                    <div className="five row" id="addr0" key={idx} >
                                        <div className="one wide column">
                                            <div className="field">
                                                <div className="ui icon" style={{ backgroundColor: "#f76060", color: "white", height: 35, width: 40, textAlign: "center", borderRadius: 2 }} onClick={() => this.handleRemoveRow(idx)}>
                                                    <i className="trash alternate icon" style={{ marginTop: 8 }}></i>
                                                </div>
                                            </div>
                                        </div>

                                        <div className="five wide column">
                                            <div className="field">
                                                <input type="text" name="text" placeholder="text" value={this.state.rows[idx].text} onChange={this.handleChange(idx)} />
                                            </div>
                                        </div>

                                        <div className="three wide column">
                                            <div className="field">
                                                <select className="ui fluid selection search dropdown " name="tax" id="tax_dropdown" value={this.state.rows[idx].tax} onChange={this.handleChange.bind(this)}>
                                                    <option defaultValue="">Select</option>
                                                    <option value="STAX">STAX</option>
                                                    <option value="VAT">VAT</option>
                                                </select>
                                            </div>
                                        </div>

                                        <div className="three wide column">
                                            <div className="field">
                                                <input type="text" name="amount" placeholder="amount" value={this.state.rows[idx].amount} onChange={this.handleChange(idx)} />
                                            </div>
                                        </div>
                                    </div>

                                ))}
                                <div className="six row">
                                    <div className="two wide column">
                                        <div className="field">
                                            <div className="ui icon" style={{ backgroundColor: "#c4d3d3", height: 35, width: 55, textAlign: "center", borderRadius: 2 }} onClick={this.handleAddRow}>
                                                <i className="plus icon" style={{ marginTop: 8 }}></i>
                                                <label>Add</label>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div className="one wide computer one wide tablet one wide mobile column">
                </div>
            </div>

        </div>
    );
}

} }

I believe to have found a much simpler way to do this by using the useRef() hook.我相信通过使用 useRef() 钩子已经找到了一种更简单的方法来做到这一点。 You simply keep a reference to the form which has the variable number of inputs, and use a button for adding and removing inputs.您只需保留对具有可变输入数量的表单的引用,并使用按钮来添加和删除输入。 Then on submit, you use the childNodes() function to get all the values.然后在提交时,您使用 childNodes() 函数来获取所有值。

form.js:表单.js:

import React, { useState, useRef } from 'react'

const Form = () => {
  const formRef = useRef(null)
  const [numInputs, setNumInputs] = useState(1)

  const submitHandler = () => {
    let inputs = formRef.current.childNodes
    for (let i = 0; i < numInputs; i++) {
      console.log(inputs[i].value)
    }
  }

  return (
    <form ref={formRef}>
      {Array.from(Array(numInputs)).map((_, i) => (
        <input type="text" />
      ))}
    </form>
    <button onClick={() => setNumInputs(numInputs - 1)}>Remove input</button>
    <button onClick={() => setNumInputs(numInputs + 1)}>Add input</button>
    <button onClick={submitHandler}>Submit</button>
  )
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM