3

React.js: Add/Remove input field on click of a button:

When a user click Add, I want a new input field to be added.

The name property is changing for every input, by increment the number in the middle:

document-0-document document-1-document

I receive the following error:

"TypeError: this is undefined var arr = this.state.documents;"

I have an idea what is creating the error but I didn't found a fix.

HTML Code.

<fieldset class="fieldset">
  <input type="file" name="document-0-document">
  <div class="more-documents">
    <input type="file" name="document-1-document">
    <button data-reactid=".0.1">Add</button>
  </div>
 </fieldset>

Main Component code:

class DocumentsFieldSet extends Component{

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

  add(i) {
      var arr  = this.state.documents;
      arr.push(i);
      this.setState({documents: arr});
  }

  eachDocument () {
      return <DocumentInput key={i}/>
  }

  render (){
      return (
        <div>
            {this.state.documents.map(this.eachDocument)}
            <button onClick={this.add.bind()}>Add</button>
        </div>
      )
  }
}

ReactDOM.render(<DocumentsFieldSet/>, document.querySelector ('.more-    documents'))

Component Code

class DocumentInput extends Component {
  render() {
      return <input type="file" name="document-{i}-document" ref="" />;
  }
}

export default DocumentInput;
1
  • What you want to add? Commented Jul 26, 2016 at 13:05

3 Answers 3

13

You have several mistakes in your example

  1. You need bind this for .add method

  2. You don't put to this.state.documents array index

  3. Inside DocumentInput there is no i variable

class DocumentInput extends React.Component {
  render() {
    return <input 
      type="file" 
      name={ `document-${ this.props.index }-document` } 
    />;
  }
}

class DocumentsFieldSet extends React.Component{
  constructor(props){
    super(props);

    this.state = { 
      documents: []
    }
    
    this.add = this.add.bind(this);
  }

  add() {
    const documents = this.state.documents.concat(DocumentInput);
    this.setState({ documents });
  }
  
  render () {
    const documents = this.state.documents.map((Element, index) => {
      return <Element key={ index } index={ index } />
    });

    return <div>
      <button onClick={ this.add }>Add</button>
    
      <div className="inputs">
        { documents }
      </div>
    </div>
  }
}

ReactDOM.render(
  <DocumentsFieldSet />,
  document.getElementById('container')
);
.inputs {
  margin: 5px 0;
  padding: 10px;
  border: 2px solid #000;
}
<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"></div>

Sign up to request clarification or add additional context in comments.

2 Comments

Delete particular filed set after clicking add button just like + -. Is it possible.
Can you help me, how to delete component using index ?
3

Maybe you forgot to pass context into bind function.

replace

<button onClick={this.add.bind()}>Add</button>

to

<button onClick={this.add.bind(this)}>Add</button>

3 Comments

This is the right answer, why has this been downvoted?
I understand that it's not ideal, because it will create a brand new function on every single render, but it's correct! :)
@PaoloMoretti you're right but I've just fixed his problem :)
0

Write your add function as an arrow function. It would bind function to this for you. Read more about it here

add = (i) => {
    var arr = this.state.documents;
    arr.push(i);
    this.setState({documents: arr});
};

Also I would rewrite your function to be:

add = (i) => {
    const { documents } = this.state;
    documents.push(i);
    this.setState({ documents });
};

1 Comment

This is a class field, it's not part of ES2015 and has to manually enabled in .babelrc by adding stage-1 or transform-class-properties

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.