tatsu
tatsu

Reputation: 2536

Add Inputs in React with add button

I'm trying to make a set of inputs that can be duplicated or removed.

I've found and used a combination of this : https://jsfiddle.net/69z2wepo/36745/ and this (because the code above does not handle removal) : https://codepen.io/lichin-lin/pen/MKMezg

I may not need to point at a specific input since in my interface you should always only be adding a new one if the one before is filled (i'll set up the condition later) and therefore only deleting the last. So I'm fine with a simple counter as a solution to all this (though I'll need 3-4 counters for the different input types).

/* ************************************* */
/* ********       IMPORTS       ******** */
/* ************************************* */
import React, { Component } from 'react';
import { Card, CardBlock, Button, InputGroup, Input } from 'reactstrap';
import ProviderInfos from '../ProviderInfos/ProviderInfos';

/* ************************************* */
/* ********      VARIABLES      ******** */
/* ************************************* */

const count = 0;

/* ************************************* */
/* ********      COMPONENT      ******** */
/* ************************************* */
export default class SearchExtendedComponent extends Component {

    constructor(props) {
        super(props);
        this.state = { inputList: [] };
        this.incrementCount = this.incrementCount.bind(this);
        this.decrementCount = this.decrementCount.bind(this);
    }

    incrementCount() {
      const inputList = this.state.inputList;
        this.setState({
            count: this.state.count + 1,
            inputList: inputList.concat(<Input key={count} />),
        });
    }
    decrementCount() {
      const inputList = this.state.inputList;
        this.setState({
            count: this.state.count - 1,
            inputList: inputList.concat(<Input key={count} />),
        });
    }
    render() {
        return (
            <Card>
                <CardBlock className="main-table">
                    <InputGroup>
                        <Input placeholder="Type1" />
                        <ProviderInfos />
                    </InputGroup>
                    {/* THE IDEA IS TO HAVE AN ADD AND REMOVE BUTTON FOR EACH TYPE */}
                    <InputGroup className="add-more">
                        <button onClick={this.incrementCount}>Add input</button>
                        {this.state.inputList}
                    </InputGroup>
                    <InputGroup>
                        <Input placeholder="Type2" />
                        <ProviderInfos />
                    </InputGroup>
                    <InputGroup>
                        <Input placeholder="Type3" />
                        <ProviderInfos />
                    </InputGroup>
                    <Button color="secondary">Options</Button>{' '}
                    <Button id="btn">Exécuter</Button>
                </CardBlock>
            </Card>
        );
    }
}

I get an error in the console :

Warning: flattenChildren(...): Encountered two children with the same key, `1:$0`. Child keys must be unique; when two children share a key, only the first child will be used.

and this seems fitting with what occurs :

Only the first new input is added after that the "add" button ceases to work.

As you can see I'm currently declaring the input within the functions ( or at least it seems to me like I am... Is that not what const inputList = this.state.inputList; does?) and I think that's the issue I should be declaring it next to "count" IMO but my attempts to do that :

const inputList = this.state.inputList;

const propTypes = { inputList: React.PropTypes.inputList, };

have resulted in the app not loading at all : "Cannot read property 'state' of undefined".

Not only that but that doesn't seem like clean refactored code to me since I'm doing it twice and keep in mind I'm going to have to duplicate this code even more since both the add and delete functions (and buttons) will have to be there for three to four different input types down the line.

UPDATE : moved the remove button part to another question : React : Add / Remove components with buttons : Remove not working

Upvotes: 1

Views: 2934

Answers (3)

abdul
abdul

Reputation: 1581

add count in the initial state.

this.state = { inputList: [] , count :0}; 

Then remove this line const count = 0;

Since you're using count as your key change this also to.

<Input key={this.state.count} />

I made a jssfiddle for another question, the concept is similar so it might be helpful.

Upvotes: 2

Prakash Sharma
Prakash Sharma

Reputation: 16472

Remove const count and initialize a count variable in state.

constructor(props) {
        super(props);
        this.state = { inputList: [], count: 0 };
        this.incrementCount = this.incrementCount.bind(this);
        this.decrementCount = this.decrementCount.bind(this);
    }

Then use this.state.count as key in input element:

incrementCount() {
      const inputList = this.state.inputList;
        this.setState({
            count: this.state.count + 1,
            inputList: inputList.concat(<Input key={this.state.count} />),
        });
    }
    decrementCount() {
      const inputList = this.state.inputList;
        this.setState({
            count: this.state.count - 1,
            inputList: inputList.concat(<Input key={this.state.count} />),
        });
    }

Upvotes: 2

Atty
Atty

Reputation: 801

The key that is passed in <Input key={count} /> should be unique. You shouldn't have two items in the list with the same key value.

In your case, when you do 2 subsequent increment and decrement, you end up with same count value. That means same key repeated for different <Input/> tag

console.log the count to check if its unique. The key 0 is used twice in the array, that's what the error means.

Upvotes: 2

Related Questions