#Problem with deleting 'completed' tasks

45 messages · Page 1 of 1 (latest)

slender mortar
#

Server.js

import express from "express";
import mysql from "mysql";
import cors from "cors";
import { promisify } from "util";

const app = express();
app.use(cors());
app.use(express.json());

const database = mysql.createConnection({
  host: "localhost",
  user: "root",
  password: "",
  database: "taskdb",
});

const query = promisify(database.query).bind(database);

app.get("/", async (req, res) => {
  try {
    const sql = "SELECT * FROM tasks";
    const data = await query(sql);
    res.json(data);
  } catch (err) {
    console.error("Error fetching tasks:", err);
    res.status(500).json(err);
  }
});

app.post("/", async (req, res) => {
  const sql = "INSERT INTO tasks (name) VALUES (?)";
  const value = req.body.name;
  try {
    const data = await query(sql, [value]);
    res.json(data);
  } catch (err) {
    console.error("Error inserting task:", err);
    res.status(500).json(err);
  }
});

app.delete("/tasks/:id", async (req, res) => {
  const { id } = req.params;
  const sql = "DELETE FROM tasks WHERE id = ?";

  try {
    const data = await query(sql, [id]);
    res.json(data);
  } catch (err) {
    res.status(500).json(err);
  }
});

app.put("/tasks/:id", async (req, res) => {
  const { id } = req.params;
  const { name, completed } = req.body;
  const sql = "UPDATE tasks SET name = ?, completed = ? WHERE id = ?";

  try {
    const data = await query(sql, [name, completed, id]);
    res.json(data);
  } catch (err) {
    console.error("Error updating task:", err);
    res.status(500).json(err);
  }
});

app.listen(9000, () => {
  console.log("Listening");
});
late quest
#
      taskData.map(async (task) => {
        await axios.delete(`http://localhost:9000/tasks/${task.id}`);
      });
      setCompletedTasks([]);
#

setCompletedTasks() does not wait for the array of promises to resolve

#
  • don't use .map() for side-effects.. use for..of (or forEach() if you must)
  • if you are using .map(), you'd have to use await Promise.all() with it
  • surely you can fix your API to allow deleting multiple IDs with a single request?
slender mortar
#

May i ask why you don't wanna use .map for side effects? What are the benefits of using for of?

late quest
burnt prism
#

Typically

const results = await Promise.allSettled(taskData.map(task => axios.delete(`http://localhost:9000/tasks/${task.id}`)));

for (const [ i, result ] of results.entries()) {
  if (result.status === "fulfilled") {
    displayToUser(`Task ${i} deleted. ${result.value}`);
  } else if (result.status === "rejected") { // note that the status can either be fulfilled or rejected so the `if` is not useful, but it's to show you the signature
    displayErrorToUser(`Failed to delete task ${i}. ${result.reason}`);
  }
}
slender mortar
#

Todoform:

    e.preventDefault();
    console.log({ task });
    setTask(" ");

    if (!task.trim()) {
      //Legge til error message
      console.warn("Kan ikke være tom");
      return;
    }

    const newTask = {
      name: task.trim(),
      completed: false,
    };

    try {
      const res = await axios.post("http://localhost:9000/", newTask);
      const createdTask = res.data;

      if (!createdTask.id) {
        throw new Error("Task ID is missing or invalid");
      }

      setTaskData([...taskData, createdTask]);
    } catch (err) {
      console.log(err);
    }
  };

Server.js:

app.post("/", async (req, res) => {
  const sql = "INSERT INTO tasks (name) VALUES (?)";
  const value = req.body.name;
  try {
    const data = await query(sql, [value]);
    res.json(data);
  } catch (err) {
    console.error("Error inserting task:", err);
    res.status(500).json(err);
  }
});

While the functions all mostly work now, there's a problem with generating a keyprop on what i do in the handlesubmit section. And i believe it to be the reason why its not rendering properly? In the database it do get an ID, so when i refresh the site it does appear but it shows an error where it doesn't get a keyprop.

#

Where i thought it generated the key:

<div className="taskSection">
        {showCompleted ? (
          <ul className="taskListItems">
            {completedTasks.map((task) => (
              <li key={task.id}>
                {task.name}
                <button onClick={() => handleDelete(task.id)}>Fjern</button>
              </li>
            ))}
          </ul>
        ) : (
          <ul>
            {taskData.map((task) => (
              <li className="taskListItems" key={task.id}>
                <button onClick={() => handleCompleteTask(task.id)}>
                  Fullført
                </button>
                {task.name}
                <button onClick={() => handleDelete(task.id)}>Fjern</button>
              </li>
            ))}
          </ul>
        )}
      </div>
      <div className="deleteAllCompletedTasksBtn">
        <button className="deleteAllBtn" onClick={handleDeleteCompletedTasks}>
          Slett Fullført Oppgaver
        </button>
      </div>
    </>
  );
}
late quest
#

pro to: add "js" after the first three backticks for syntax highlighting

#

what does it say doesn't have a key?

slender mortar
#

{task: 'Hello'}
TodoForm.js:140 Warning: Each child in a list should have a unique "key" prop.

Check the render method of TodoForm. See link for more information.
at li
at TodoForm (link)
at div
at App

late quest
#

what's line 140?

#

I'd guess you have multiple tasks with the same ID

slender mortar
#
 <div className="taskSection">
        {showCompleted ? (
          <ul>
            {completedTasks.map((task) => (
              <li className="taskListItems" key={task.id}>
                {task.name}
                <button onClick={() => handleDelete(task.id)}>Fjern</button>
              </li>
            ))} <--- Line 140
          </ul>
        ) : (
late quest
#

Then ya, two tasks with same id

#

```js
Code here
```

slender mortar
#

Sending for clarity, but it seems like the first one always gets an error but the error doesn't show up later one. Even if the task names are unique.

#
const handleSubmit = async (e) => {
    e.preventDefault();
    console.log({ task });
    setTask(" ");

    if (!task.trim()) {
      //Legge til error message
      console.warn("Kan ikke være tom");
      return;
    }

    const newTask = {
      name: task.trim(),
      completed: false,
    };

    try {
      const res = await axios.post("http://localhost:9000/", newTask);
      const createdTask = res.data;

      setTaskData([...taskData, createdTask]);
    } catch (err) {
      console.log(err);
    }
  };

    useEffect(() => {
    const fetchData = async () => {
      try {
        const res = await axios.get("http://localhost:9000/");
        const allTasks = res.data;

        // Separate completed and incomplete tasks
        const completed = allTasks.filter((task) => task.completed);
        const incomplete = allTasks.filter((task) => !task.completed);

        setTaskData(incomplete); // For incomplete tasks
        setCompletedTasks(completed); // For completed tasks
      } catch (err) {
        console.log(err);
      }
    };
    fetchData();
  }, []);

#
  return (
    <>
      <form onSubmit={handleSubmit}>
        <h1 className="taskHeader">TODOLIST</h1>
        <div className="submitSectionHeader">
          <input
            className="taskInput"
            type="text"
            placeholder="Legg til en oppgave"
            value={task}
            onChange={(e) => setTask(e.target.value)}
          />
          <button className="submitBtn" type="submit">
            Legg til
          </button>
        </div>
      </form>
      <div className="taskStatusBtns">
        <button className="tasksBtn" onClick={() => setShowCompleted(false)}>
          Oppgaver
        </button>
        <button
          className="completedTasksBtn"
          onClick={() => setShowCompleted(true)}
        >
          Ferdige Oppgaver
        </button>
      </div>
      <div className="taskSection">
        {showCompleted ? (
          <ul>
            {completedTasks.map((task) => (
              <li className="taskListItems" key={task.id}>
                {task.name}
                <button onClick={() => handleDelete(task.id)}>Fjern</button>
              </li>
            ))}
          </ul>
        ) : (
          <ul>
            {taskData.map((task) => (
              <li className="taskListItems" key={task.id}>
                <button onClick={() => handleCompleteTask(task.id)}>
                  Fullført
                </button>
                {task.name}
                <button onClick={() => handleDelete(task.id)}>Fjern</button>
              </li>
            ))}
          </ul>
        )}
      </div>
      <div className="deleteAllCompletedTasksBtn">
        <button className="deleteAllBtn" onClick={handleDeleteCompletedTasks}>
          Slett Fullført Oppgaver
        </button>
      </div>
    </>
  );
}
#

server.js:

import express from "express";
import mysql from "mysql";
import cors from "cors";
import { promisify } from "util";

const app = express();
app.use(cors());
app.use(express.json());

const database = mysql.createConnection({
  host: "localhost",
  user: "root",
  password: "",
  database: "taskdb",
});

const query = promisify(database.query).bind(database);

app.get("/", async (req, res) => {
  try {
    const sql = "SELECT * FROM tasks";
    const data = await query(sql);
    res.json(data);
  } catch (err) {
    console.error("Error fetching tasks:", err);
    res.status(500).json(err);
  }
});

app.post("/", async (req, res) => {
  const sql = "INSERT INTO tasks (name) VALUES (?)";
  const value = req.body.name;
  try {
    const data = await query(sql, [value]);
    res.json(data);
  } catch (err) {
    console.error("Error inserting task:", err);
    res.status(500).json(err);
  }
});
#

Newest code

late quest
#

look at the rendered HTML are all the keys present and unique?

slender mortar
#

From looking at the database and deleting the data 1 by 1 console logging the id deleted, they were all unique

#

But when im creating the key prob and try to console log task.id then it says undenified but console logging just task works.

late quest
#

why are you avoiding what I said to do? I don't care what the database says. I don't care what the fetch says. look at what is in the HTML itself

#

if it says task.id is undefined, that's obviously your issue

#

that means all your keys are "undefined"

slender mortar
#

I am sorry about that, avoiding it was very much not my purpose as i am trying to understand this. i am unfamiliar with how i check the HTML for keys, so i jumped to the place where i knew where to find keys. And then console logged it afterwards.

late quest
#

actually I'm an idiot. I was thinking they'd show in the HTML within devtools but they don't...

#

my bad. logging each task.id works as well, though

slender mortar
#

All good, im very appreciative of you helping out. You've been very helpful in trying to understand this and make it work. But this one problem got me a bit stunned

#

Could this be of any help? It seems like if i for example use setTaskData([...taskData, createdTask]); but use newTask instead of createdTask then at least it renders correctly but i still get the key prop message.

late quest
#

you said task.id is undefined... that is your issue

slender mortar
#

Yes sir, but im not sure how to fix it as i've tried at least what i can come up with right now. Both from the backend and in the js code but i will keep hammering it on until something works hehe

late quest
#
res.json(data);

show data

#

wait..

      const res = await axios.post("http://localhost:9000/", newTask);
      const createdTask = res.data;

      setTaskData([...taskData, createdTask]);

this is why... createdTask is not a task...

#
    const data = await query(sql, [value]);
    res.json(data);

log data and you'll clearly see it doesn't match the format you think it should (no .id)

slender mortar
#

I am not sure how to see console logs from the backend as it does not show up in the normal console.log. How would i fix this mess i've made and make createdTask into a task? I don't understand how its not a task

late quest
#

you look at the console where you ran the backend, not the browser

#

this image is what query("INSERT INTO tasks (name) VALUES (?)", [value]) returns.
however, an actual task would be something like the following (based on your screenshot):

{
  "id": 198,
  "name": "Great Dress"
}
#

looking at mysql/mysql2 docs, I'm not seeing any way to have an insert query return the inserted item. so, you'll just have to SELECT WHERE id=${data.insertId}, I guess?

slender mortar
#

Ahh okey i see, then i got something to think about on how to move forward from here. For the time being im using newTask as it at least renders correctly but gives the same error message hehe. My mind is absolutely fried.

#

Would it at all be possible with a discord call and go over and see? I hate to be such a pain in the ass for you right now