1

Unable to render a child component inside main Component using iteration

I have a main component defined as App.js

import "./styles.css";
import { Component } from "react";

import Item from "./components/Item";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = { textInput: "", items: [] };
  }
  insertItem() {
    if (this.state.textInput !== "") {
      this.setState((state) => {
        const list = state.items.push(state.textInput);
        return {
          items: list,
          textInput: ""
        };
      });
    }
  }
  deleteItem(index) {
    this.setState((state) => {
      const list = state.items.splice(index, 1);
      return {
        items: list,
        textInput: ""
      };
    });
  }
  handleChange(event) {
    this.setState({ textInput: event.target.value });
  }
  render() {
    const template = (
      <div>
        <div>
          <input
            type="text"
            value={this.state.textInput}
            onChange={(e) => this.handleChange(e)}
          />
          <button onClick={this.insertItem.bind(this)}>Add</button>
        </div>
        <div>
          {this.state.items.map((item, idx) => {
            return <Item name={item} removeItem={this.deleteItem(idx)} />;
          })}
        </div>
      </div>
    );
    return template;
  }
}
export default App;

and a child Component defined in Item.js

import { Component } from "react";
class Item extends Component {
  render() {
    return (
      <div>
        <span>{this.props.name}</span>
        <span onClick={this.props.removeItem}>X</span>
      </div>
    );
  }
}
export default Item;

Now my UI looks likeenter image description here

In the above code(App.js) Iam trying to iterate the items and then display the names using the child component. But due to some reason, on entering the text in the input and clicking add its not showing up. Also there are no errors in the console.

Please help, thanks in advance

Edited:

After the recent changes I get this error enter image description here

enter image description here

Submitted June 06th 2021 by Admin

Answers
0

Updating state in react requires a new reference to objects.You're using Array#push. It will not detect your new change and the DOM will not update. You need to return a new reference.

 insertItem() { if (this.textInput === "") { this.setState((state) => { // const list = state.items.push(state.textInput); const list = [...state.items, state.textInput]; return { list, textInput: "" }; }); } }

Admin | 3 months ago


0

In order to track the array, you must add the key attribute:

 {this.state.items.map((item, idx) => { return <Item key={idx} name={item} removeItem={this.deleteItem(idx)} />; })}

Here I used the index, but it would be better to use some ID of your model.

UPDATE:

I'd move the handler binding in the constructor:

 constructor(props) { super(props); this.state = { textInput: "", items: [] }; this.insertItem = this.insertItem.bind(this); }

Then:

 <button onClick={this.insertItem}>Add</button>

UPDATE 2:

Okay, it seems you have several mistakes (and I didn't notice them at first glance).

Here is the complete working source (tested):

class App extends Component { constructor(props) { super(props); this.state = { textInput: "", items: [] }; this.insertItem = this.insertItem.bind(this); this.deleteItem = this.deleteItem.bind(this); this.handleChange = this.handleChange.bind(this); } insertItem() { if (this.state.textInput !== "") { this.setState((state) => { //const list = state.items.push(state.textInput); const list = [...state.items, state.textInput]; return { items: list, textInput: "" }; }); } } deleteItem(index) { this.setState((state) => { const list = state.items.splice(index, 1); return { items: list, textInput: "" }; }); } handleChange(event) { this.setState({ textInput: event.target.value }); } render() { const template = ( <div> <div> <input type="text" value={this.state.textInput} onChange={(e) => this.handleChange(e)} /> <button onClick={this.insertItem}>Add</button> </div> <div> {this.state.items.map((item, idx) => { return <Item key={idx} name={item} removeItem={() => this.deleteItem(idx)} />; })} </div> </div> ); return template; }
}
export default App;

Admin | 3 months ago


0

You do not need to call this.deleteItem(idx) while passing it to the child.

import "./styles.css";
import { Component } from "react"; import Item from "./components/Item"; class App extends Component { constructor(props) { super(props); this.state = { textInput: "", items: [] }; } insertItem() { if (this.state.textInput !== "") { this.setState((state) => { const list = state.items.push(state.textInput); return { items: list, textInput: "" }; }); } } deleteItem(index) { this.setState((state) => { const list = state.items.splice(index, 1); return { items: list, textInput: "" }; }); } handleChange(event) { this.setState({ textInput: event.target.value }); } render() { const template = ( <div> <div> <input type="text" value={this.state.textInput} onChange={(e) => this.handleChange(e)} /> <button onClick={this.insertItem.bind(this)}>Add</button> </div> <div> {this.state.items.map((item, idx) => { return <Item name={item} removeItem={this.deleteItem.bind(this, idx)} />; })} </div> </div> ); return template; }
}
export default App;

Admin | 3 months ago


0

I think you should move display of template into return statement rather than inside render

...
render() { return ( <div> <div> <input type="text" value={this.state.textInput} onChange={(e) => this.handleChange(e)} /> <button onClick={this.insertItem.bind(this)}>Add</button> </div> <div> {this.state.items.map((item, idx) => { return <Item name={item} removeItem={this.deleteItem(idx)} />; })} </div> </div> );
}

Admin | 3 months ago



Relevant Questions




Deleting a todo - MERN Stack

September 10th 2021