Search code examples
javascriptnode.jsreactjscodesandbox

How to splice an array within the state of a React Component


I am making a timesheet app. The index.js renders the table. The rows of the table are read from the children array. The children array reads the state to keep itself updated. AddRow() is working fine. DeleteRow() has some problem. I intend to fetch the parentNode id from the event, lookup the state's array, splice it and let children update itself - which in turn updates the render. So when a row is deleted, it is spliced from the state's array, and that in turn is updated in the children array, and that in turn is rendered. DeleteRow runs everything except the array.splice or indexOf part. I even tried a for loop, but it doesn't work. Is my approach correct?

index.js    
import React from "react";
import ReactDOM from "react-dom";
import HeaderAndFirstTableRow from "./headerandfirstrow";
import TableRow from "./tablerow";
import "./styles.css";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { noOfRows: [] };
    this.addRow = this.addRow.bind(this);
    this.deleteRow = this.deleteRow.bind(this);
    this.find = this.find.bind(this);
  }
  addRow() {
    let arr = this.state.noOfRows;
    let arrLength = arr.length;
    arr.push(arrLength + 1);
    this.setState({
      noOfRows: arr
    });
  }
  find(arr, id) {
    for (let i = 0; i < arr.length; i++) {
      if (arr[i] === id) {
        return i;
      }
    }
  }
  deleteRow(event) {
    let arr = this.state.noOfRows;
    let id = event.target.parentNode.id;
    let ix = arr.findIndex(id);
    arr.splice(ix, 1);
    this.setState({ noOfRows: arr });
  }
  render() {
    let children = [];
    for (let i = 0; i < this.state.noOfRows.length; i++) {
      children.push(
        <TableRow id={this.state.noOfRows.length} delete={this.deleteRow} />
      );
    }
    return (
      <HeaderAndFirstTableRow click={this.addRow}>
        {children}
      </HeaderAndFirstTableRow>
    );
  }
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

headerandfirstrow.js

import React from "react";

import "./styles.css";

export default function HeaderAndFirstTableRow(props) {
  function handler() {
    props.click();
  }
  return (
    <table>
      <tr>
        <th>Feature</th>
        <th>Phase</th>
        <th>Comments</th>
        <th>Monday</th>
        <th>Tuesday</th>
        <th>Wednesday</th>
        <th>Thursday</th>
        <th>Friday</th>
        <th>Saturday</th>
        <th>Sunday</th>
        <th onClick={handler}>+</th>
      </tr>

      {props.children}
    </table>
  );
}

tablerow.js
import React from "react";
import "./styles.css";
export default function TableRow(props) {
  function del(e) {
    props.delete(e);
  }
  return (
    <tr id={props.id}>
      <td />
      <td />
      <td />
      <td />
      <td />
      <td />
      <td />
      <td />
      <td />
      <td />
      <td onClick={del}>-</td>
    </tr>
  );
}

Solution

  • splice mutates original array, you should not mutate state,

    You can change to this

    deleteRow(event) {
        let arr = this.state.noOfRows;
        let id = event.target.parentNode.id;
        let filteredArray = arr.filter(val=> val !== id)
        this.setState({ noOfRows: filteredArray });
    }