Home > Mobile >  advice with removing object from react state array
advice with removing object from react state array

Time:09-08

So this has me puzzled. I've been banging my head against the wall trying to figure this out.

So I am trying to remove an object from a state managed array. I don't believe I am mutating the array.

I am using prevState. My delete function which gets sent to another component

{this.state.educationArray.map((item, i) => (<RenderEducation education={item} onDelete={this.handleDelete} />))}

Sending back the id to the handleDelete function.

My handleDelete:

handleDelete = itemId => {
  //const newStudy = this.state.educationArray.filter(item => { return item.id !== itemId });
  //this.setState({ educationArray: newStudy })

  let tempArray = [];
  let num = this.state.educationArray.length;

  for (let i = 0; i < num;) {
    //console.log("itemId is: ", itemId)
    let tempId = this.state.educationArray[i].id
    if (tempId != itemId) {
      let obj = this.state.educationArray[i]
      tempArray.push(obj)
    }
    i  
  }
  this.setState(prevState => ({ educationArray: tempArray }));
}

Stack Snippet w/loop:

const { useState } = React;

class Example extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            educationArray: [
                { id: 1, name: "One" },
                { id: 2, name: "Two" },
                { id: 3, name: "Three" },
            ],
        };
    }

    handleDelete = (itemId) => {
        // const newStudy = this.state.educationArray.filter(item => { return item.id !== itemId });
        // this.setState({ educationArray: newStudy })

        let tempArray = [];
        let num = this.state.educationArray.length;

        for (let i = 0; i < num; ) {
            //console.log("itemId is: ", itemId)
            let tempId = this.state.educationArray[i].id;
            if (tempId != itemId) {
                let obj = this.state.educationArray[i];
                tempArray.push(obj);
            }
            i  ;
        }
        this.setState((prevState) => ({ educationArray: tempArray }));
    };

    render() {
        return (
            <ul>
                {this.state.educationArray.map((element) => (
                    <li key={element.id}>
                        {element.name}{" "}
                        <input type="button" value="Del" onClick={() => this.handleDelete(element.id)} />
                    </li>
                ))}
            </ul>
        );
    }
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

Stack Snippet w/filter:

const { useState } = React;

class Example extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            educationArray: [
                { id: 1, name: "One" },
                { id: 2, name: "Two" },
                { id: 3, name: "Three" },
            ],
        };
    }

    handleDelete = (itemId) => {
        const newStudy = this.state.educationArray.filter(item => { return item.id !== itemId });
        this.setState({ educationArray: newStudy })
        /*

        let tempArray = [];
        let num = this.state.educationArray.length;

        for (let i = 0; i < num; ) {
            //console.log("itemId is: ", itemId)
            let tempId = this.state.educationArray[i].id;
            if (tempId != itemId) {
                let obj = this.state.educationArray[i];
                tempArray.push(obj);
            }
            i  ;
        }
        this.setState((prevState) => ({ educationArray: tempArray }));
        */
    };

    render() {
        return (
            <ul>
                {this.state.educationArray.map((element) => (
                    <li key={element.id}>
                        {element.name}{" "}
                        <input type="button" value="Del" onClick={() => this.handleDelete(element.id)} />
                    </li>
                ))}
            </ul>
        );
    }
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

I've tried using the 2 lines commented out, I've tried rearranging how I do the for loop, its always the same result, it never removes the intended id.

I have sent console.log after console.log of the ids getting moved around and every seems to be working, but when it comes right now to push the specific objects that don't match the id to the temp array it never works and the object add the end gets removed.

Please and thank you for your advice

EDIT: i call the handleDelete inside RenderEducation component:

<button onClick={() => this.props.onDelete(this.state.id)}> X - {this.state.id}</button>

from each and my constructor:

constructor(props) {
            super(props);
            this.state = {
                  educationArray: [],

            }
      }

and how i add to the array:

addEducation = (e) => {
            e.preventDefault();
            this.setState(prevState => ({
                  educationArray: [...prevState.educationArray, {
                        id: uniqid(),
                        school: '',
                        study: '',
                        dateFrom: '',
                        dateTo: '',
                        editing: true,
                  }]
            }))
      }

CodePudding user response:

Both versions of your code work in regular, non-edge-case situations, as we can see from the Stack Snippets I added to your question. The only problem I can see with the code shown is that it's using a potentially out-of-date version of the educationArray. Whenever you're updating state based on existing state, it's best to use the callback form of the state setter and use the up-to-date state information provided to the callback. Both of your versions (even your loop version, which does use the callback) are using this.state.educationArray instead, which could be out of date.

Instead, use the array in the state passed to the callback:

handleDelete = (itemId) => {
    // Work with up-to-date state via the callback
    this.setState(({educationArray: prevArray}) => {
        // Filter out the element you're removing
        return {
            educationArray: prevArray.filter(({id}) => id !== itemId)
        };
    });
};

Live Example:

const { useState } = React;

class Example extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            educationArray: [
                { id: 1, name: "One" },
                { id: 2, name: "Two" },
                { id: 3, name: "Three" },
            ],
        };
    }

    handleDelete = (itemId) => {
        // Work with up-to-date state via the callback
        this.setState(({educationArray: prevArray}) => {
            // Filter out the element you're removing
            return {
                educationArray: prevArray.filter(({id}) => id !== itemId)
            };
        });
    };

    render() {
        return (
            <ul>
                {this.state.educationArray.map((element) => (
                    <li key={element.id}>
                        {element.name}{" "}
                        <input type="button" value="Del" onClick={() => this.handleDelete(element.id)} />
                    </li>
                ))}
            </ul>
        );
    }
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

CodePudding user response:

I have always ran into problems with arrays as state, it might be a problem that you are referencing to the original array in the state.

I believe copying it to another array, modifying it then setting the state would maybe help you:

const originalEducationArray = [...this.state.educationArray];

const newStudy = originalEducationArray.filter(item => { return item.id !== itemId })

this.setState({ educationArray: newStudy })
  • Related