Djaenike
Djaenike

Reputation: 1865

React js to refresh page after onClick has been called

I've been following one of the MERN stack tutorials online (making a simple todo app), and decided to go off-script a little bit. I wanted to add a button to delete a specific item. The delete function is working fine, however it requires the user to manually refresh the page after they click the delete button in order to see the new list of elements in my database (MongoDB). I'd like the page to automatically refresh after the click event, however I'm not sure where to start. Within the react render there is a table, which references a variable to actually assemble the components of the table - this is where the delete button exists. Here is my code:

import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';

const Todo = props => (
    <tr>
        <td className={props.todo.todo_completed ? 'completed' : ''}>{props.todo.todo_title}</td>
        <td className={props.todo.todo_completed ? 'completed' : ''}>{props.todo.todo_description}</td>
        <td className={props.todo.todo_completed ? 'completed' : ''}>{props.todo.todo_responsible}</td>
        <td className={props.todo.todo_completed ? 'completed' : ''}>{props.todo.todo_priority}</td>
        <td>
            <Link to={"/edit/"+props.todo._id}>Edit</Link>
        </td>
        <td>
            {/* this is where the delete happens */}
            <button onClick={ () =>
                axios.delete('http://localhost:4000/todos/'+props.todo._id)
                    .then(console.log("Deleted: " + props.todo._id))                    
                    .catch(err => console.log(err))
            }
            >Delete</button>
        </td>
    </tr>
)

export default class TodosList extends Component {

    constructor(props) {
        super(props);
        this.state = {todos: []};
    }

    componentDidMount() {
        axios.get('http://localhost:4000/todos/')
            .then(res => {
                this.setState({ todos: res.data });
            })
            .catch(function(err){
                console.log(err);
            })
    }

    todoList() {
        return this.state.todos.map(function(currentTodo, i){
            return <Todo todo={currentTodo} key={i} />;
        })
    }

    render() {
        return (
            <div>
                <h3>Todos List</h3>
                <table className="table table-striped" style={{ marginTop: 20 }} >
                    <thead>
                        <tr>
                            <th>Title</th>
                            <th>Description</th>
                            <th>Responsible</th>
                            <th>Priority</th>
                            <th>Action</th>
                            <th>Remove Item</th>
                        </tr>
                    </thead>
                    <tbody>
                        { this.todoList() }
                    </tbody>
                </table>
            </div>
        )
    }
}

Hopefully someone on here can get me pointed in the right direction.

Thanks

Upvotes: 1

Views: 11095

Answers (3)

Sahil Raj Thapa
Sahil Raj Thapa

Reputation: 2473

You can delete the specific item from TodosList component state after you have successfully deleted the item from Todo component. For that you can

1) add a method in TodosList component.

deleteItemHandler = (id) => {
 const updatedTodos = this.state.todos.filter(todo => todo.id !== id);
 this.setState({todos: updatedTodos})
}

2) pass the method deleteItemHandler as props to Todo component

todoList() {
    return this.state.todos.map((currentTodo, i) => {
       return <Todo todo={currentTodo} deleteItem={this.deleteItemHandler} key={i} />;
   })
}

3) use it after item is successfully deleted

<td>
   {/* this is where the delete happens */}
   <button onClick={ () =>
       axios.delete('http://localhost:4000/todos/'+props.todo._id)
          .then(() => props.deleteItem(props.todo._id))                    
          .catch(err => console.log(err))
    }
    >Delete</button>
</td>

Another way

Instead deleting item from TodosList component you can also update the state. For that you can

1) add method that updates in TodosList component

 updateStateHandler = () => { 
     axios.get('http://localhost:4000/todos/')
        .then(res => {
             this.setState({ todos: res.data });
        })
        .catch(function(err){
            console.log(err);
       })
  }

2) pass the method updateStateHandler as props to Todo component

todoList() {
    return this.state.todos.map((currentTodo, i) => {
       return <Todo todo={currentTodo} updateState={this.updateStateHandler} key={i} />;
   })
}

3) use it after item is successfully deleted

<td>
   {/* this is where the delete happens */}
   <button onClick={ () =>
       axios.delete('http://localhost:4000/todos/'+props.todo._id)
          .then(() => props.updateState())                    
          .catch(err => console.log(err))
    }
    >Delete</button>
</td>

Upvotes: 4

Murtaza Hussain
Murtaza Hussain

Reputation: 4285

You need to do this

export default class TodosList extends Component {

    constructor(props) {
        super(props);
        this.state = {todos: []};
        this.fetchTodos = this.fetchTodos.bind(this);
    }

    fetchTodos() {
        axios.get('http://localhost:4000/todos/')
            .then(res => {
                this.setState({ todos: res.data });
            })
            .catch(function(err){
                console.log(err);
            });
    }

    componentDidMount() {
         this.fetchTodos();
    }

    todoList() {
        return this.state.todos.map((currentTodo, i) => {
            return <Todo todo={currentTodo} fetchTodos={this.fetchTodos}  key={i} />;
        })
    }
   ...

Todo:

        <td>
            {/* this is where the delete happens */}
            <button onClick={ () =>
                axios.delete('http://localhost:4000/todos/'+props.todo._id)
                    .then(() => {
                         console.log("Deleted: " + props.todo._id);
                         props.fetchTodos();
                     })                    
                    .catch(err => console.log(err));
            }
            >Delete</button>
        </td>

Upvotes: 1

The authority that renders the table is your TodosList class, so it needs to be told to do the deleting:

class TodosList extends ... {
  ...
  todoList() {
    return this.state.todos.map((currentTodo, i) => {
      let onDelete = () => {
        this.removeItem(i);
      };
      // NEVER use an array position as key. The key is meant to uniquely
      // identify the _item itself_ and is used in DOM diffing. Moving elements
      // inside an array does not change those elements in the slightest and only
      // requires moving DOM nodes around, but if you use array position as key,
      // what you've now done is said that _everything in the DOM node has changed_
      // So: don't do that. Use a real, item based, value.
      return <Todo todo={currentTodo} key={currentTodo.id} onDelete={onDelete}/>;

      // Of course this assumes todo items have an `id` property.
      // If they don't, pick another property _on the todo item_ that
      // uniquely identifies it.
    });
  }

  removeItem(i) {
    let todos = this.state.todos;
    todos.splice(i,1);
    // This single call now results in all the UI updates that you
    // need to have happen: the todo item is no longer in the state,
    // and so its DOM node will be removed from the page. And because
    // we're now using real keys, React will not touch any of the other
    // DOM nodes. The UI update is near-instant.
    this.setState({ todos });
  }
  ...
}

Then the individual buttons can call their own onDelete once deleting has happened:

const deleteThisItem = () => {
  axios
  .delete('http://localhost:4000/todos/'+props.todo._id)
  .then(this.props.onDelete())                    
  .catch(err => console.log(err))
};

<button onClick={deleteThisItem}>delete</button>

So the flow is:

  • TodoList knows all the todo items,
  • TodoList generates UI for each todo item,
  • item UI includes a button that will call TodoList's item deletion function, which will update the TodoList state
  • simply by virtue of removing an item from that state and calling setState with that change, React will render what needs to be rendered.

Upvotes: 0

Related Questions