0

I have a constructor in my main component:

class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      items: []
    } 
  };

  render() {
    return (
      <div className="App">
        <ItemList  items={this.state.items}/>       
        <AddItemForm items={this.state.items}/>
      </div>
    );
  }
}

In component AddItemForm I'm adding to array items objects with properties "item_name" that is string and "comment" with data type object. View of component:

class AddItemForm extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      item:{}  
    };
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({item:
     {
      item_name: event.target.value,
      comment:{}
      }
    });

  }

  handleSubmit(event) {
    event.preventDefault();
    this.props.items.push(this.state.item);
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          <input type="text" item_name={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

export default AddItemForm;

How can I iterate this array to get all item_name values of every object and display them as list in my ItemList component?

Stepan Kovalyshyn
  • 186
  • 1
  • 2
  • 12

3 Answers3

1

This should help.

class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      items: []
    } 
  };

  addItemToItemsList = (item) => {
    const {items=[]} = this.state;
    items.push(item);
    this.setState({
      items : items
    });
  }

  render() {
    return (
      <div className="App">
        <ItemList items={this.state.items}/>       
        <AddItemForm
          items={this.state.items}
          addItemToItemsList={this.addItemToItemsList}
        />
      </div>
    );
  }
}

class ItemList extends React.Component {
  render () {
    const {items} = this.props;
    return (
      <div>
        {items.map((item, index) => {
          return (
            <div key={index}>item.item_name</div>
          )
        })}
      </div>
    );
  }
}

class AddItemForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      item: {
        item_name : '',
        comment:{}
      }  
    };
  }

  handleChange = (event) => {
    const new_item = Object.assign({}, this.state.item, {item_name: event.target.value});
    this.setState({
      item: new_item
    });
  }

  handleSubmit = (event) => {
    event.preventDefault();
    this.props.addItemToItemsList(this.state.item);
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          <input type="text" item_name={this.state.item.item_name} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}
export default AddItemForm;
Nidhi Agarwal
  • 316
  • 2
  • 13
  • Thank you for answer. This helped me. I took your example and changed item.item_name to {item.item_name} at ItemList component and it started work correct. – Stepan Kovalyshyn Dec 11 '17 at 11:27
0

I think, you have an error inside AddItemForm, you should pass onSubmit function from App to AddItemForm and change items through this function:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      items: []
    }

    this.handleSubmit = this.handleSubmit.bind(this);
  };

  handleSubmit(value){
    this.setState({
      items: this.state.items.concat(value)
    })
  }

  render() {
    return (
      <div className="App">
        <ItemList items={this.state.items} />
        <AddItemForm 
          onSubmit={this.handleSubmit}
          items={this.state.items} />
      </div>
    );
  }
}

About main question, one of the way to solve this problem

const ItemList = ({items}) => (
  <div>
    {items.map( (item, index)=> (
      <div key={index}>{item.item_name}</div>
    ))}
  </div>
);

full working example here: https://codesandbox.io/s/7k624nz94q

Slawa Eremin
  • 5,264
  • 18
  • 28
0

You can't add to the array directly. You need to pass a callback that will add to the array in your parent component's state. This is a very common pattern when using react.

Here is a skeleton of what you need to do:

In your parent component, you don't need to pass the whole list to your AddItemForm component, just a addItem callback to your child component:

class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      items: []
    }
    this.addItemToList = this.addItemToList.bind(this); 
  };

  render() {
    return (
      <div className="App">
        <ItemList  items={this.state.items}/>       
        <AddItemForm addItemToList={this.addItemToList}/>
      </div>
    );
  }

  addItemToList(newValue) {
    // Here you add the item to your state
    // Always treat your state as immutable, so create a copy then add the item, then set your new State
    const newArray = this.state.items.slice(); // clone
    newArray .push(newValue); // Add value
    this.setState({items: newArray}); // Set the new state
  }
}

More info on how to add items to an array in the state here: React.js - What is the best way to add a value to an array in state

Then you use that callback in your child component:

  handleSubmit(event) {
    event.preventDefault();
    // this.props.items.push(this.state.item);
    // Here don't mutate the props, instead call the callback to add the item to your parent's component's state
    this.props.addItemToList(this.state.item);
  }

To display a list of items, you need to use the map function: https://reactjs.org/docs/lists-and-keys.html#rendering-multiple-components

klugjo
  • 19,422
  • 8
  • 57
  • 75