2

I have a process where the comments associated to a book record are stored in an array in a state and has actions to POST or DELETE an individual record and array element. I have the POST action working as intended and the DELETE API call working to delete my record from a database, but I'm not sure the best practice to search my array for the newly deleted record and to remove that value from the array or if I should trigger a refresh method for the React component to display the newly changed array.

Parent state and functionality (question is related to deleteCommentsFunc():

class BookFeed extends React.Component {
    constructor(props){
        super(props);
        this.state = { 
            comments: []
        };
        this.deleteCommentsFunc = this.deleteCommentsFunc.bind(this);
    }

    componentWillReceiveProps(nextProps){
        console.log("componentWillReceiveProps");
        console.log(nextProps);
        let commentArr = [];
        nextProps.books.map((book) => {
            book.book_comments.map((comment) => {
                commentArr.push(comment);
            })
        })
        console.log(commentArr)
        this.setState({comments: commentArr});
    }

    deleteCommentsFunc(deletedComment){
        console.log(deletedComment);
        var updatedCommentArr = this.state.comments;
        var index = updatedCommentArr.indexOf(deletedComment.target.value);
        updatedCommentArr.splice(index, 1);
        this.setState({comments: updatedCommentArr});
    }

    render(){
        return (
            <div>
            { 
                this.props.books.map((book, index) => {
                    return (
                        <div className="row">
                            <div className="col-md-6 col-md-offset-3 book-card-container">
                                <BookCard {...book} key={book.bookIdHash} user={this.props.user} />
                                <Comments comments={this.state.comments} key={index} bookId={book.bookIdHash} csrf={this.props.csrf} updateComments={this.updateCommentsFunc} deleteCommentFunc={this.deleteCommentsFunc} user={this.props.user.userId}/> 
                            </div>
                        </div>
                    );
                })
            }
            </div>
        );
    }
}

Child function calling DELETE and passing returned JSON (which is just a 1 indicating a successful delete):

deleteCommentCall(bookId, bookCommentId, csrf){
        var body = { bookId: bookId, bookCommentId: bookCommentId };
        var route = 'http://localhost:3000/app/book/' + bookId + '/comment/' + bookCommentId;
        fetch(route, 
            { 
                method: 'DELETE', 
                body: JSON.stringify(body), 
                headers: { 
                    'X-CSRF-Token': csrf,
                    'Accept': 'application/json',
                    'Content-Type': 'application/json'
                }
            })
            .then(res => {
                return res.json();
            })  
            .then(data => {
                console.log(data);
                this.props.deleteCommentFunc(data)
                this.setState({'flash': 'success'});
            }) 
            .catch(err => {
                console.log(err);
                this.setState({'flash': 'error'});
            });
    }

As you might be able to tell the parameter value data, which contains a 1 if DELETE was successful is not helpful in my deleteCommentsFunc(), and what I really need to have is a value that I can find within the array to be able to remove it.

cphill
  • 5,596
  • 16
  • 89
  • 182

2 Answers2

2

Directly calling splice() on this.state in React is bad practice as it is a mutative method.

You could retain your current implementation by cloning your comments array using spread syntax, rather than pointing to a reference of it.

// Comments.
const comments = [...this.state.comments]

Alternatively, you could utilize Array.prototype.filter() to achieve the same objective with less overhead.

deleteCommentsFunc(bookCommentId){

  // Filter Out Deleted Comment By Comparing Id.
  const comments = this.state.comments.filter((comment) => { 

    // bookCommentId.
    console.log('bookCommentId', bookCommentId)

    // comment.bookCommentId.
    console.log('comment.bookCommentId', comment.bookCommentId)

    // Compare.
    comment.bookCommentId != bookCommentId)
  }

  // Update State.
  this.setState({comments}) 
}

Please note: I have no idea what your comment schema is (I'm assuming they are objects with unique id props). You will have to pass the bookCommentId to the deleteCommentsFunc() when you call it and maybe adjust the comment.id reference therein to point to your comment's actual id prop.

Arman Charan
  • 5,669
  • 2
  • 22
  • 32
  • Arman thank you for your detailed answer. I believe your note at the end of your answer is very relevant to the problem I face with using your `deleteCommentsFunc()`. I have the passed the deleted ID to the function and is stored as `bookCommentId`, but my `comment.bookCommentId` is not matching so the array does not change. Here is the Schema from my JSON: `{bookCommentId: 4, bookId: 5, comment: "This is a test"}`. Shouldn't `comment.bookCommentId` be the correct reference? Is there a way to see what `comment` is referencing out of the `.filter()` method? – cphill Dec 12 '17 at 12:23
  • Yes mate, that is correct. `bookCommentId` should be compared with `comment.bookCommentId`. See my revised answer for an example of how to log the `filter()` method. – Arman Charan Dec 12 '17 at 12:37
  • Thanks for the update, the logging has been helpful in seeing the array being looped through each `comment.bookCommentId` and I see the Id that matched `bookCommentId`, but the `//Update State. this.setState({comments})` still shows the same array length as when the array started. I'm using `componentWillReceiveProps(nextProps)` to load reload the state when a new record is added, should something happen here when a record is deleted in order to show the change of removing the record? – cphill Dec 13 '17 at 02:09
  • 1
    Disregard my comment above. It was because I was filtering on a variable set to `this.state.comments` rather than setting a variable to the output of the `.filter()`. Thanks for your help! – cphill Dec 13 '17 at 03:02
0

Since you have a comment ID, it is better to filter the comments array: filter will return a new array.

deleteCommentsFunc(deletedCommentID){
   var updatedCommentArr = this.state.comments;
   updatedCommentArr.filter(comment => comment.bookCommentId !== deletedCommentID)
   this.setState({comments: updatedCommentArr});
}

Inside deleteCommentCall, you can pass the ID: this.props.deleteCommentFunc(bookCommentId)

OSAMAH
  • 139
  • 3