1

I want to pass a template as props or as children and everytime I click 'add' i want to add one more item.

This is where I got so far:

App.js

<List template={
  (<Item>
      <span>Test</span>
   </Item>)
} />

List.js

...
add = () => {
    const list = this.state.list;
    const i = list.length + 1;
    const newItem = cloneElement(this.props.template, { key: i });
    this.setState({ list: this.state.list.concat([newItem]) });
}

...

render() {
    return (
      <div className="list">
        {this.state.list}
        <button onClick={this.add}>Add</button>
      </div>
    );
  }

But altough a new item is added, it has no children, so the 'Item' is there, but the 'span' is not. So the list is always empty.

Anyone knows how to do something like that?

The point is that the List mustn't be aware of what is passed to it. Whatever you pass, it should add.

Victor Ferreira
  • 6,151
  • 13
  • 64
  • 120
  • https://stackoverflow.com/questions/36651583/dynamically-add-child-components-in-react – Ramesh Aug 18 '18 at 03:52
  • not sure if it is the same problem. in that case SampleComponent is defined in the App component. What I want is the App component to now know about SampleComponent. It must receive it as props – Victor Ferreira Aug 18 '18 at 03:57

2 Answers2

2

Your List component only renders children. I basically do something like below

 class List extends Component{
     render(){
         return(
             <div>
                 {this.props.children}
             </div>
         )
    }
}

And you can call List and pass items or whatever like below

Your button is in the component where you call List and you pass onClick={this.add} to the button. Something like below

 this.state ={
      items: []
 }
 add = () => {
      const item =  <Item>
           <span>Test</span>
         </Item>
      this.setState(prevState => ({
           items: [...prevState.items, item]
      }));
  }
 <div>
      <List>
          {items}
      </List>
      <button onClick={this.add} />
   </div>
Jasperan
  • 2,154
  • 1
  • 16
  • 40
Hemadri Dasari
  • 32,666
  • 37
  • 119
  • 162
  • but after pressing 'add' it should add one new 'Item'. This logic must be inside the List component – Victor Ferreira Aug 18 '18 at 03:58
  • I updated my answer please check. This will add new item whenever add button is clicked – Hemadri Dasari Aug 18 '18 at 04:12
  • i just tried and what happened was what I said that the list becomes empty because the children of the child (in this case ) is not there. – Victor Ferreira Aug 18 '18 at 04:19
  • 1
    @VictorFerreira This is because of the component.You must make sure that the component renders its children which were passed as a prop childrend. `const Item = props => (
    {props.children}
    )`.@Hemadri Dasari has provided you an elegant solution for your problem.He did it with proper way how to update state based on the state.If you want to use the add logic in the component you can pass the logic through the props.I think that the best idea how to improve your code would be to show all of it here or on code review.Cheers!
    – Jan Ciołek Aug 18 '18 at 05:01
0
import React, { Component } from 'react';

class Item extends Component {

  render(){
    return (<p>This is an item
      {this.props.children}
    </p>)
  }

}

class List extends Component {

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

  add = () => {
    const list = this.state.list;
    const i = list.length + 1;
    const newItem = React.cloneElement(this.props.template, { key: i });
    this.setState({ list: this.state.list.concat([newItem]) });
    console.log(this.props.template);
  }

  render() {
    return (
      <div className="list">
        {this.state.list}
        <button onClick={this.add}>Add</button>
      </div>
    );
  }

}

class App extends Component {
  render() {
    return (
      <List template={
        (<Item>
            <span>This is a Template</span>
         </Item>)
      } />
    );
  }
}

export default App;
Alex M
  • 690
  • 4
  • 18