1

I am new to ReactJS. When I click on the button, the call will go from createTablesam.js to index.js to update the object. the object is updated successfully in setState but not updating in UI. If I again the button again the object is updating but UI is not updating.

const PureComponent = React.PureComponent;

class Index extends PureComponent {
  constructor() {
    super();
    this.state = {
      userValues: [
        {
          name: "Mani",
          age: 23,
          skill: "Java"
        },
        {
          name: "Vel",
          age: 23,
          skill: "react"
        }
      ]
    };
    this.addRow = this.addRow.bind(this);
  }

  addRow() {
    let newdetails = {
      name: "ManiVEL",
      age: 25,
      skill: "REACT JS"
    };
    let userValues = this.state.userValues;
    userValues.push(newdetails);
    this.setState({
      userValues
    });
  }

  render() {
    return (
      <CreateTablesam
        addtable={this.addRow}
        tabelDeatils={this.state.userValues}
      ></CreateTablesam>
    );
  }
}

class CreateTablesam extends PureComponent {
  state = {
    tableCopy: this.props.tabelDeatils
  };

  render() {
    const createRow = this.state.tableCopy.map(per => (
      <tr key={per.name}>
        <td>{per.name}</td>
        <td>{per.age}</td>
        <td>{per.skill}</td>
      </tr>
    ));

    return (
      <div>
        <table>
          <thead>
            <tr>
              <td>Name</td>
              <td>Age</td>
              <td>skills</td>
            </tr>
          </thead>
          <tbody>{createRow}</tbody>
        </table>
        <button onClick={this.props.addtable}>CLICK</button>
      </div>
    );
  }
}

ReactDOM.render(<Index />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

what mistake have I done?

Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
  • 1
    Does this answer your question? [Correct modification of state arrays in ReactJS](https://stackoverflow.com/questions/26253351/correct-modification-of-state-arrays-in-reactjs) – maazadeeb Nov 25 '19 at 17:52
  • Don't store it in state, just read it from props directly in `CreateTablesam ` and it will work as expected – bamtheboozle Nov 25 '19 at 17:57

5 Answers5

0

You need to improve the way you are updating array in state. As mentioned in comment section, please read Correct modification of state arrays in ReactJS

In your case, it would be: this.setState({ userValues: [...this.state. userValues, newelement] })

Once you have above point right, you need to improve key handling part. Please read: Understanding unique keys for array children in React.js

Reason for it is that data in table have same key key={per.name}. Since you are always adding the same records, name property will remain same. So, <tr key={per.name}> key will remain same. Try adding different object(at least different name) to table.

mukesh210
  • 2,792
  • 2
  • 19
  • 41
0

You're directly mutating the state in addRow. Either make a copy of the userValues or use the functional form of setState:

addRow() {
  let newdetails = {
    name: "ManiVEL",
    age: 25,
    skill: "REACT JS"
  };

  this.setState(state => {
    return {
      userValues: [...state.userValues, newdetails]
    }
  });
}

// or

addRow() {
  let newdetails = {
    name: "ManiVEL",
    age: 25,
    skill: "REACT JS"
  };

  this.setState({
      userValues: [...this.state.userValues, newdetails]
  });
}

Edit: you also need to use the props directly, instead of assigning them to state in your child component:

class CreateTablesam extends PureComponent {
  render() {
    const createRow = this.props.tabelDeatils.map((per, i) => (
      <tr key={i}>
        <td>{per.name}</td>
        <td>{per.age}</td>
        <td>{per.skill}</td>
      </tr>
    ));

    return (
      <div>
        <table>
          <thead>
            <tr>
              <td>Name</td>
              <td>Age</td>
              <td>skills</td>
            </tr>
          </thead>
          <tbody>{createRow}</tbody>
        </table>
        <button onClick={this.props.addtable}>CLICK</button>
      </div>
    );
  }
}
Clarity
  • 10,730
  • 2
  • 25
  • 35
  • That still won't work as he's storing the initial props in state and never updating them. Common antipattern in react. – bamtheboozle Nov 25 '19 at 17:58
0

React.PureComponent’s shouldComponentUpdate() skips prop updates for the whole component subtree. Make sure all the children components are also “pure”.

Thats why your props not updating your components. Try it with React.Components

Neo
  • 467
  • 1
  • 3
  • 17
0

You dont have to copy props into state in Child component.

class CreateTablesam extends PureComponent {
  render() {
    const createRow = this.props.tabelDeatils.map(per => (
      <tr key={per.name}>
        <td>{per.name}</td>
        <td>{per.age}</td>
        <td>{per.skill}</td>
      </tr>
    ));

    return (
      <div>
        <table>
          <thead>
            <tr>
              <td>Name</td>
              <td>Age</td>
              <td>skills</td>
            </tr>
          </thead>
          <tbody>{createRow}</tbody>
        </table>
        <button onClick={this.props.addtable}>CLICK</button>
      </div>
    );
   }
  }

Also please make sure you are adding unique items in addtable function

addRow() {
    let newdetails = {
      name: "ManiVEL"+ Math.random(),
      age: 25,
      skill: "REACT JS"
    };
    this.setState({
      userValues: [...this.state.userValues, newdetails]
    });
  }
Subin Sebastian
  • 10,870
  • 3
  • 37
  • 42
0

Two problems

  1. Don't edit the existing array, rather create a new one.
  2. this.state = ... is run only once per instance of the component. And since you're storing the tabelDeatils value there, it will never get updated.

The changes are as below

const PureComponent = React.PureComponent;

class Index extends PureComponent {
  constructor() {
    super();
    this.state = {
      userValues: [
        {
          name: "Mani",
          age: 23,
          skill: "Java"
        },
        {
          name: "Vel",
          age: 23,
          skill: "react"
        }
      ]
    };
    this.addRow = this.addRow.bind(this);
  }

  addRow() {
    let newdetails = {
      name: "ManiVEL",
      age: 25,
      skill: "REACT JS"
    };
    this.setState({
      userValues: [...this.state.userValues, newdetails]
    });
  }

  render() {
    return (
      <CreateTablesam
        addtable={this.addRow}
        tabelDeatils={this.state.userValues}
      ></CreateTablesam>
    );
  }
}

class CreateTablesam extends PureComponent {
  render() {
    const createRow = this.props.tabelDeatils.map((per, idx) => (
      <tr key={idx}>
        <td>{per.name}</td>
        <td>{per.age}</td>
        <td>{per.skill}</td>
      </tr>
    ));

    return (
      <div>
        <table>
          <thead>
            <tr>
              <td>Name</td>
              <td>Age</td>
              <td>skills</td>
            </tr>
          </thead>
          <tbody>{createRow}</tbody>
        </table>
        <button onClick={this.props.addtable}>CLICK</button>
      </div>
    );
  }
}

ReactDOM.render(<Index />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

EDIT

As mentioned in the comment, you need to add unique keys when generating elements in a loop. I've added the index in the array, but it's not advised to do so. You need to find something that is unique to each element and pass it as the key.

maazadeeb
  • 5,922
  • 2
  • 27
  • 40