0

I am trying to update the rendered cards everytime the checkbox is checked/unchecked. Currently I am having trouble for quite some time trying to figure out how to update the todos state which is an array containing multiple JSON as such:

[{"_id":"fhrgiu34hnt","todo_description":"Lunch","todo_responsible":"1:30PM","todo_priority":"High","todo_completed":true,"todo_id":"fhrgiu34hnt"},{"_id":"fgvjhbansfgki35sn","todo_description":"Dinner","todo_responsible":"7:45PM","todo_priority":"High","todo_completed":true,"todo_id":"fgvjhbansfgki35sn"},{"_id":"sfgvhio3t58","todo_description":"Supper","todo_responsible":"10:30PM","todo_priority":"Medium","todo_completed":false}]

The values gets updated when changedBox is called via onChange but does not re-render because of (wrongly) trying to compare the todos in the componentDidUpdate? The updated values of the checkbox would be rendered again when I manually refresh the page. Is there a way I can update the checked todos in the component Todo so that it would render correctly?

Here's what I have at the moment:

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

const changedBox = (e, props) => {

    const completed = e;
    console.log("Inside changedBox");
    console.log("Trying to update - ID: " + props.todo._id);
    console.log("Checkbox is currently: " + completed);
    const obj = {
        todo_id: props.todo._id,
        todo_description: props.todo.todo_description,
        todo_responsible: props.todo.todo_responsible,
        todo_priority: props.todo.todo_priority,
        todo_completed: completed
    };
    axios.post('example.com/api', obj)
        .then(response => console.log(response.data));
};

function Todo (props) {
    let [t] = useState(props.todo);
    console.log(t)
    console.log(JSON.stringify(t));
    useEffect(() => {
        console.log("Inside useEffect");
        axios.get('example.com/api')
            .then(response => {
                t = props.todo;
            })
            .catch(function (error) {
                console.log(error);
            })

    })

    return (
        <div className="card" key={props.todo._id}>
            <div className="card-body">

                <input type="checkbox" id={props.todo._id} checked={props.todo.todo_completed}
                       onChange={event => changedBox(!props.todo.todo_completed, props)}/>
                <label htmlFor={props.todo._id}/>

                <div className="card-title">
                    <h4> {props.todo.todo_description} </h4>
                    <h6> {props.todo.todo_responsible} </h6>
                    <h6> {props.todo._id ? props.todo._id : "No ID yet"} </h6> {/* Debugging */}
                    <h6> {props.todo.todo_priority} </h6> {/* Debugging */}
                    <span
                        className="badge badge-info"> {props.todo.todo_completed ? "Completed" : "Not Completed"} </span>
                </div>

                <div className="dropdown card-options">
                    <Link to={"/edit/" + props.todo._id}
                          style={{color: 'inherit', textDecoration: 'inherit'}}>
                        <button className="btn-options" type="button">
                            <i className="fas fa-ellipsis-v"/>
                        </button>
                    </Link>
                </div>

            </div>

        </div>

    )
}

class TodosList extends Component {
    _isMounted = false;

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

    componentDidMount() {
        this._isMounted = true;
        axios.get('example.com/api')
            .then(response => {
                if (this._isMounted) {
                    this.setState({todos: response.data});
                }
            })
            .catch(function (error) {
                console.log(error);
            })
    }

    componentDidUpdate(prevProps, prevState, ss) {

        console.log("prevState.todos: " + prevState.todos);
        console.log(JSON.stringify(prevState.todos));

        console.log("Comparing JSON.stringify(this.state.todos) !== JSON.stringify(prevState.todos): " + (JSON.stringify(this.state.todos) !== JSON.stringify(prevState.todos)));

        if (JSON.stringify(this.state.todos) !== JSON.stringify(prevState.todos)) {

            console.log("Inside componentDidUpdate - Attempting to update!");
            axios.get('example.com/api')
                .then(response => {
                    this.setState({todos: response.data});
                })
                .catch(function (error) {
                    console.log(error);
                });
        }
    }

    componentWillUnmount() {
        this._isMounted = false;
    }


    todoList() {
        return this.state.todos.map(function (currentTodo, i) {
            console.log("Todo ID: " + currentTodo._id + ", Num: " + i);
            return <Todo todo={currentTodo} key={i} ind={i}/>;
        });
    }

    render() {

        return (
            <div className="card-container">
                <h3>List</h3>
                {this.todoList()}
            </div>
        )
    }
}

export default TodosList;

1 Answer 1

1

There's a few things you could do better here.

First, the useState returns array of 2 elements, the first one is the data, second - the updater functions, so whenever you're using useState and you want to update the state, do it like this:

const [data, setData] = useState('some data');

setData('I want to update it');

Then the component will re-render.

This should be fixed

.then(response => {
  // t = props.todo;
  setTodo(props.todo);
})

The changedBox method doesn't cause the rerender of the component because it's not changing the state, use setTodo again.

    const obj = {
        todo_id: props.todo._id,
        todo_description: props.todo.todo_description,
        todo_responsible: props.todo.todo_responsible,
        todo_priority: props.todo.todo_priority,
        todo_completed: completed
    };

setTodo(obj)

I see that you're using mix of hooks and class based components, try to use one of them, it will make your code more consistent.

Sign up to request clarification or add additional context in comments.

1 Comment

I have managed to resolve the issue by converting to just class component and using setState. Thanks for the suggestion!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.