0

I have the following class component:

class ConceptPopup extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            attributeForm: []
        };
        this.addAttributeForm();
    }

    addAttributeForm() {
        //this method adds label and input field to the state array 

        var array = this.state.attributeForm;

        array.push(<div>
                        <span><b>new Attribute</b></span>
                        <div>
                             <label htmlFor="name">Attribute name :</label>
                             <input name="name"/>
                        </div>
                   </div>
        );

        this.setState({
            attributeForm: array
        });
    }

    render() {
        return (
            <div>
                {this.state.attributeForm}
                <button onClick={this.addAttributeForm.bind(this)}>ADD ATTRIBUTE</button>
            </div>
        );
    }
}

Now the ideal situation would be that everytime the ADD ATTRIBUTE button is clicked a new input field appears.

My current situation is that the method addAttribute is called on button click and the state updates correctly but the changes aren't visible. All that is shown is one input field due to the addAttributeForm from the constructor. I tried to force re-render the form on click but that only throws a buch of errors (like Maximum call stack size exceeded).

cookieDope
  • 33
  • 2
  • 9
  • 1
    storing ui elements in state variable is not a good approach, check this answer [**dynamically creating form elements**](https://stackoverflow.com/questions/42316604/how-to-implement-a-dynamic-form-with-controlled-components-in-react-js) – Mayank Shukla Mar 22 '18 at 11:28

2 Answers2

1

You need to loop through and render your array of inputs

render() {
      return (
          <div>
              { 
                this.state.attributeForm.map(input => {
                    return input
                })
              }
              <button onClick={this.addAttributeForm.bind(this)}>ADD ATTRIBUTE</button>
          </div>
      );
  }
Stretch0
  • 8,362
  • 13
  • 71
  • 133
1

Okay well there's a few points here:

  1. When updating an array in state, you need to make a copy of the array using something like slice and set the state to that, don't reference it directly cos then you'll be directly updating the state (i.e. var array = this.state.attributeForm.slice();)
  2. Don't store entire components in the state, store the bare bones information needed to render them (like their properties and the number of them)
  3. Be aware that without keys you could run into issues with arrays of components and updating them
  4. If you want to start with 1 component in the array, just set it that way in the constructor's this.state={} call. Don't set the array to empty, then call then the addAttributeForm() function, unnecessary step
  5. As Stretch0 said above, you need to loop through arrays and render each element
  6. Even if you're just playing around and learning stuff, usually good to give proper names. Calling the new array of form attributes array can cause confusion. Better to use something explicit like var newAttributeForm = this.state.attributeForm.slice();
  7. While not necessary, it's usually cleaner and easier to bind all your functions in the constructor. That way you can keep track of their bindings and don't potentially lose the binding when deleting some function or changing an element or something
Jayce444
  • 8,725
  • 3
  • 27
  • 43