-1

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,
                  }]
            }))
      }
  • Hi! Although that's not how I would suggest doing it (see: https://pastebin.com/zqGdF3tz), it should mostly work, at least in non-edge conditions. It sounds like `itemId` isn't what you think it is. Please update your question with a [mcve] demonstrating the problem, ideally a **runnable** one using Stack Snippets (the `[<>]` toolbar button). Stack Snippets support React, including JSX; [here's how to do one](http://meta.stackoverflow.com/questions/338537/). – T.J. Crowder Sep 08 '22 at 06:10
  • I also suggest [debugging](https://stackoverflow.com/questions/25385173/) to find out exactly what's going on in your loop. People will tell you to stumble around in the dark with a `console.log` torch, but I find it's much better to *turn on the lights* using the debugger built into your IDE and/or browser. – T.J. Crowder Sep 08 '22 at 06:11

1 Answers1

0

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>
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • I copied this to my code and i still get the same result. it always removes the last object of the array – Chris Williamson Sep 08 '22 at 06:30
  • @ChrisWilliamson - Then `itemId` identifies the last item in the array. I suspect the problem is with how you're calling `handleDelete`. Please update the question to show how you're doing that, you probably have the [closure in loops problem](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) in it. – T.J. Crowder Sep 08 '22 at 06:32
  • I think updated my question to show what you were asking, as for the loop, the only for loop i had was int he handleDelete function which was removed when i tried using your version. – Chris Williamson Sep 08 '22 at 06:42
  • im also using uniqid to generate id's for each object that gets put into the array, i dont think that matters though – Chris Williamson Sep 08 '22 at 06:45
  • @ChrisWilliamson - I'm afraid I can't tell from the question what's wrong. You'll need to update it to provide a [mcve]. Fundamentally, the code above is the correct way to remove the element with an `id` matching `itemId` from an array in state. If it's not working (and in fact, if your original two versions aren't working), it's a problem elsewhere, it's just impossible to tell where from what's in the question. It'll be something in `RenderEducation` around its `id` state member, but... :-) – T.J. Crowder Sep 08 '22 at 06:48
  • I'll build a code snippet and resubmit it later...i just dont understand. I've checked the id thats being passed. i've checked the typeof on the id's and they all come back string, – Chris Williamson Sep 08 '22 at 07:01