1

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

9
  • add count in the initial state. this.state = { inputList: [] , count :0}; remove the const count = 0; line Commented Aug 7, 2017 at 9:45
  • @abdul is the initial state a default function of sorts that I4m supposed to add? I don't see it in my own code. Commented Aug 7, 2017 at 9:50
  • you're setting the initial state of your component just below this line super(props) inside your constructor. just add count : 0 to it like this.state = { inputList: [] , count :0}; Commented Aug 7, 2017 at 9:53
  • @abdul ok but with const count = 0; removed I get Uncaught ReferenceError: count is not defined Commented Aug 7, 2017 at 9:55
  • I think you're getting that b/c you're using in <Input key={count} /> change this also to <Input key={this.state.count} /> Commented Aug 7, 2017 at 9:58

3 Answers 3

2

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.

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

8 Comments

I don't see any button that calls the decrement method. add this ` <InputGroup className="remove"> <button onClick={this.decrementCount}>Add input</button> {this.state.inputList} </InputGroup>`
thanks! could you explain how a refactored version of this with say 4 different cards would look?
I had added the button already but it doesn't trigger. why do you add a second {this.state.inputList}
ohh that's by mistake, if you've the button it should work as expected.
ideally, you could have a different list and count for each input type. Also a different handler(increment and decrement) for each input type that increments the corresponding input type count.
|
2

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.

6 Comments

ok. but how do I not give 0 to the "key" whatever that is. I feel like I've done nothing to make that happen? count should be this.state.count + 1, .... 0+1 =1 and so on....
dont use count at all in input tag. Initialise another variable and keep incrementing it in case of both increment and decrement
ok thanks but I can't jsut put inputList: inputList, at some point I have to in some desciption tell inputList to "+1" do I not? what does that look like? I really am a beginner in react.
If you really need the count, then use it as another parameter. So your tag could look like <Input key={some_unique_key} count={count}/>
thanks! abdul suggested something similar and it worked :)
|
2

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} />),
        });
    }

1 Comment

the decrement method does not work for me. I get : warning.js:36 Warning: flattenChildren(...): Encountered two children with the same key, 0:$0. Child keys must be unique; when two children share a key, only the first child will be used and the "remove" button makes the list of inputs bigger by one and then stops doing anything.

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.