I am building a todo app using PERN(postgres, Express, React, Node). Iam almost there, however I got stuck in this issue.
I have attached the images at the bottom.
The issue is my application seems to render twice due to my componentDidMount? (Not sure) I have looked at some posts, but still i do not understand how to get around this problem.
What I want is that when I click edit button, it pops up a modal with input box to edit a todo. and the input box needs to have the value before editing. however what i get now is always the first todo, even though you click the second todo in the list.
Side note, i would like to use class component for EditTodo.jsx
I highly appreciate if you could tell me why I get such outputs and provide me the exact solution to this issue. Thank you so much in advance.
ListTodo.jsx
import React, { useEffect, useState } from "react";
const ListTodos = () => {
const [todos, setTodos] = useState([]);
const deleteTodo = async id => {
try {
const deleteTodo = await fetch(`http://localhost:5000/todos/${id}`, {
method: "DELETE"
});
console.log(deleteTodo);
setTodos(todos.filter(todo => todo.todo_id !== id))
} catch(err) {
console.error(err.message);
}
}
const getTodos = async () => {
try {
const response = await fetch("http://localhost:5000/todos")
const jsonData = await response.json()
console.log(jsonData);
setTodos(jsonData)
}catch (err) {
console.error(err.message);
}
}
useEffect(() => {
getTodos();
}, [])
return(
<>
<table className="centered highlight striped">
<thead>
<tr className="centered">
<th>Description</th>
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{
todos.map(todo => (
<tr key={todo.todo_id}>
<td>{ todo.description }</td>
<td>
<EditTodo todo={todo} />
{/* this todo does not pass iteratively */}
</td>
<td>
<button
className="btn waves-effect waves-red red darken-4"
onClick={() => deleteTodo(todo.todo_id)}
>
Delete
</button></td>
</tr>
))
}
</tbody>
</table>
</>
)
}
export default ListTodos;
EditTodo.jsx
import React from 'react';
import M from "materialize-css";
export default class EditTodo extends React.Component {
constructor(props) {
super (props);
this.state = {
todo_id: this.props.todo.todo_id,
description: this.props.todo.description
}
}
componentDidMount() {
const options = {
onOpenStart: () => {
console.log("Open Start");
},
onOpenEnd: () => {
console.log("Open End");
},
onCloseStart: () => {
console.log("Close Start");
},
onCloseEnd: () => {
console.log("Close End");
},
inDuration: 250,
outDuration: 250,
opacity: 0.5,
dismissible: false,
startingTop: "4%",
endingTop: "10%"
};
M.Modal.init(this.Modal, options);
}
editing = e => {
console.log(e.target.value);
this.setState({
description: e.target.value
})
}
updateSubmit = async e => {
e.preventDefault();
try {
const body = this.state.description;
const id = this.state.todo_id;
if(body.description === "") throw new Error("Input cannot be empty");
// const response = await fetch(`http://localhost:5000/todos/${id}`, {
// method: "PUT",
// body: JSON.stringify(body)
// });
// console.log(response)
// window.location = "/";
} catch (err) {
console.log(err.message)
}
}
render() {
// console.log(this.props)
console.log(this.props.todo)
// console.log(this.props.children)
return (
<>
<a
className="waves-effect waves-light btn modal-trigger yellow blue-text"
data-target="modal1"
>
Edit
</a>
<div
ref={Modal => {
this.Modal = Modal;
}}
id="modal1"
className="modal"
>
<form onSubmit={this.updateSubmit}>
<div className="modal-content">
<h4>Edit a todo</h4>
<input
type="text"
onChange={this.editing}
value={this.props.todo.description}
/>
</div>
<div className="modal-footer">
<button type="submit" className="modal-close waves-effect orange waves-yellow btn-flat">Edit</button>
<a className="modal-close waves-effect green waves-red btn-flat">Close</a>
</div>
</form>
</div>
</>
)
}
}