简体   繁体   中英

React. Delete specific row from the table

I have a component that is a table. Each row of this table is also component.

    class FormulaBuilder extends Component {
        constructor(props) {
            super(props);
            this.state = {
                rows: [{}]
            }
        }
handleAddRow = () => {
        const item = {};

        this.setState({
            rows: [...this.state.rows, item]
        });
    };

    handleRemoveSpecificRow = (idx) => {
        const rows = [...this.state.rows]
        rows.splice(idx, 1)
        this.setState({ rows })
    }

     render() {
            return (
               {
                   this.state.rows.map((item, idx) => {
                        return (
                           <React.Fragment key={idx}>
                                 <ConcoctionRow
                                  removeSpecificRow={(idx) =>this.handleRemoveSpecificRow(idx)}
                                  id={idx} />
                            </React.Fragment>);
                })
             });
            }
    }

In the child component there is a button. When clicked, the event from the parent component is called:

class ConcoctionRow extends Component {
    constructor(props) {
        super(props);
    }

  handleRemoveSpecificRow = () => {
        this.props.removeSpecificRow(this.props.id);
    }
}

The properties passed the index of the array. But only the last line is always deleted not specific.

Where is my bad? PS I am new in JS.

A couple of things, you want to avoid using .splice() to update your arrays in components. Often times this actually ends up mutating your original state instead of creating a new one. A direct violation of React concepts.

Likewise lets try some stuff out on the console:

const arr = [1, 2, 3] <-- this is your state
const newArr = arr  <-- you created a reference of your state. This does not actually create a new copy.

Now if you splice the newArr

newArr.splice(0, 1) <-- now newArr = [2, 3]

Well guess what, you also mutated your original state.

arr <-- is now also [2, 3]

A common misconception in JavaScript is that when you create a new variable that equals an existing variable, you expect that it actually creates a new copy.

let cat = {id: 1, name: "bunny"}
let myCat = cat

This is not actually the case, instead of explicitly creating a new copy, your new variable points to the same reference of the original object it is derived from. If I did something like:

myCat.age = 2  <-- Now myCat has a new property of age.
myCat <-- {id: 2, name: "bunny", age: 2}

BUT, because these two variables point to the same reference. You also mutate the original cat object as well

cat <-- {id: 2, name: "bunny", age: 2}

Use array.filter() instead to create a completely new array.

Here's an example with your code as well as a sandbox for reference: https://codesandbox.io/s/heuristic-nobel-6ece5

import React from "react";
import ConcoctionRow from "./ConcoctionRow";

class FormulaBuilder extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      rows: [{}, {}, {}]
    };
  }
  handleAddRow = () => {
    const item = {};

    this.setState({
      rows: [...this.state.rows, item]
    });
  };

  handleRemoveSpecificRow = idx => {
    const { rows } = this.state;

    const updatedRows = rows.filter((row, index) => {
      return index !== idx;
    });

    this.setState({
      rows: updatedRows
    });
  };
  render() {
    return (
      <div>
        {this.state.rows.map((item, idx) => {
          return (
            <React.Fragment key={idx}>
              <ConcoctionRow
                removeSpecificRow={this.handleRemoveSpecificRow}
                id={idx}
              />
            </React.Fragment>
          );
        })}
      </div>
    );
  }
}

export default FormulaBuilder;

I show the pattern I would use for this case. I recommend to use id instead of array index for items. filter array function is immutable (it creates a new array, not mutates the previous one), so ok to use in set state. The functional form of setState is also a good stuff.

 const Row = ({ onClick, children, id }) => ( <li>{children} <button onClick={() => onClick(id)}>Delete</button></li> ) class App extends React.Component { state = { list: [ {id: 1, label: 'foo' }, {id: 2, label: 'bar' } ] } handleDelete = id => { this.setState(prevState => ({ list: prevState.list.filter(row => ( row.id !== id )) })) } render(){ const { list } = this.state; return ( <ul> {list.map(({ id, label }) => ( <Row id={id} onClick={this.handleDelete}>{label}</Row> ))} </ul> ) } } ReactDOM.render(<App />, 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> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM