Justin Simon
Justin Simon

Reputation: 17

Why is React not re-rendering on setState?

I have a React project for a todo list. When you click to delete a task, it successfully sends the request to the API and removes the task from the database. After that, I request the tasks again and then setState with the new task list. After using setState, nothing updates on the website until you refresh the page.

What am I doing wrong? I'm brand new to React so any explanation, I would be very grateful for!

App.js:

import React from 'react';
import taskApi from "../api/task-api";
import TaskList from "./TaskList";
import {Button, Container} from "react-bootstrap";

class App extends React.Component {
    state = {
        tasks: [],
    }

    componentDidMount() {
        taskApi.get('/tasks').then(
            res => {
                const tasks = res.data.data;
                this.setState({tasks})
            }
        );

    }


    render() {
        return(
            <Container>
                <h1>Task Manager</h1>
                <Button variant="primary" size="lg" className="add-task-button">Add Task</Button>
                <TaskList tasks={this.state.tasks} />
            </Container>
        );
    }
}

export default App;

TaskList.js:

import React from 'react';
import Table from 'react-bootstrap/Table'

import TaskItem from "./TaskItem";

class TaskList extends React.Component {
    render() {
        return(
                <Table striped bordered hover>
                    <thead>
                        <tr key="header">
                            <th>ID #</th>
                            <th>Task Name</th>
                            <th></th>
                        </tr>
                    </thead>
                    <tbody>
                    {this.props.tasks.map((task) => {
                        return <TaskItem task={task} key={task.id} />
                    })}
                    </tbody>
                </Table>
        );
    }
}

export default TaskList;

TaskItem.js:

import React from 'react';
import {Button, Modal} from "react-bootstrap";
import taskApi from "../api/task-api";


class TaskItem extends React.Component {

    constructor() {
        super();
        this.state = {
            show: false,
        };
    }

    showModal() {
        this.setState({show: true});
    }

    closeModal() {
        this.setState({show: false});
    }

    deleteTask(task_id) {
        this.setState({show: false});
        taskApi.delete('/task/' + task_id).then(()=>{
            taskApi.get('/tasks').then(
                res => {
                    const tasks = res.data.data;
                    this.setState({tasks})
                }
            );
            }
        );
    }

    render() {
        return(
                <tr>
                    <td>{this.props.task.id}</td>
                    <td>{this.props.task.task_name}</td>
                    <td>
                        <Button variant="success" className="task-action-button">Complete</Button>
                        <Button variant="secondary" className="task-action-button">Edit</Button>
                        <Button variant="danger" className="task-action-button" onClick={()=>{this.showModal()}}>Delete</Button>
                        <Modal show={this.state.show}>
                            <Modal.Header>Confirm</Modal.Header>
                            <Modal.Body>
                                Are you sure you want to delete this task?
                                <br/><br/>
                                <strong>Task:</strong>
                                <br/>
                                {this.props.task.task_name}
                            </Modal.Body>
                            <Modal.Footer>
                                <Button variant="secondary" onClick={()=>{this.closeModal()}}>Cancel</Button>
                                <Button variant="danger" onClick={()=>{this.deleteTask(this.props.task.id)}}>Delete</Button>
                            </Modal.Footer>
                        </Modal>
                    </td>
                </tr>
        );
    }
}

export default TaskItem;

Upvotes: 0

Views: 136

Answers (1)

CertainPerformance
CertainPerformance

Reputation: 370729

Right now, you're doing

class TaskItem extends React.Component {
    deleteTask(task_id) {
        // ...
        this.setState({tasks})

You're setting state in TaskItem - but you never reference this.state.tasks in TaskItem. The tasks state exists in the parent, inside your class App. So, updating the state in the child component doesn't do anything - you need to update the state in the parent instead.

Put the methods that need to manipulate the parent's state in the parent, and then pass them down.

class App extends React.Component {
    deleteTask = (task_id) => {
        taskApi.delete('/task/' + task_id).then(()=>{
             // ...

App's render method:

<TaskList tasks={this.state.tasks} deleteTask={this.deleteTask} />

TaskList's render method:

return <TaskItem task={task} key={task.id} deleteTask={this.props.deleteTask} />

And finally in TaskItem:

deleteTask(task_id) {
    this.setState({show: false});
    this.props.deleteTask(task_id);
}

Upvotes: 1

Related Questions