Reputation: 25
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;
Upvotes: 0
Views: 1815
Reputation: 828
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.
Upvotes: 1