Tasks Not Updating in Real Time After Add/Update in React App

26 Views Asked by At

In my React application, tasks do not update in real time after adding or updating them. I must refresh the browser to see the changes. How can I make the tasks update in real time without needing a refresh? Below is my code snippet involving task operations like add, delete, and fetch.

This is todo.jsx:

const Todo = () => {
  let id = sessionStorage.getItem("id");
  const [Array, setArray] = useState([]);
  const [Inputs, setInputs] = useState({
    title: "",
  });
  const username = localStorage.getItem("username");
  const naviagte = useNavigate();

  useEffect(() => {
    if (id === null) {
      naviagte("/login");
    }
    const fetchTasks = async () => {
      try {
        const response = await axios.get(
          `http://localhost:1000/api/v2/getTasks/${id}`
        );
        setArray(response.data.list);
      } catch (error) {
        console.error("Failed to fetch tasks:", error);
      }
    };
    fetchTasks();
  }, [id]);

  const change = (e) => {
    const { name, value } = e.target;
    setInputs({ ...Inputs, [name]: value });
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (Inputs.title === "") {
      toast.error("Enter Task");
    } else {
      if (id) {
        await axios
          .post("http://localhost:1000/api/v2/addTask", {
            title: Inputs.title,
            id: id,
          })
          .then((response) => {
            console.log(response);
          });
        console.log("task is added : ", Inputs);
        setInputs({ title: "" });
        toast.success("Task Added");
      } else {
        setArray([...Array, Inputs]);
        setInputs({ title: "" });
        toast.success("Task Added");
        toast.error("No id found");
      }
    }
  };

  const del = async (Cardid) => {
    try {
      const response = await axios.delete(
        `http://localhost:1000/api/v2/deleteTask/${Cardid}`,
        {
          data: { id: id },
        }
      );
      if (response.status === 200) {
        // Filter out the deleted task from the Array state
        setArray(Array.filter((item) => item._id !== Cardid));
        toast.success("Task Deleted");
      }
    } catch (error) {
      console.error(error);
      toast.error("Failed to delete task");
    }
  };

  // const dis = (value) => {
  //   document.getElementById("todo-update").style.display = value;
  // };
  // const onTaskUpdate = () => {
  //   fetchTasks(); // Re-fetch tasks after an update
  // };
  return (
    <div className="todo">
      <nav>
        <h1>Welcome {username} </h1>

        <button className="btnlogout" onClick={() => naviagte("/")}>
          Logout
        </button>
      </nav>

      <div className="todo-heading">
        <h1>To-Do List</h1>
      </div>

      <div className="todo-container">
        <ToastContainer></ToastContainer>
        <div className="todo-form">
          <form onSubmit={handleSubmit} style={{ display: "flex" }}>
            <input
              className="addtodoinput"
              type="text"
              placeholder="Enter task"
              name="title"
              onChange={change}
              value={Inputs.title}
            />
            <button className="add" type="submit">
              Add Task
            </button>
          </form>
        </div>
        <div className="todo-body">
          <div>
            {Array &&
              Array.map((item, index) => (
                <div key={index} className="todo-container">
                  <TodoCard
                    title={item.title}
                    id={item._id}
                    delid={del}
                    // onUpdate={onTaskUpdate}
                  />
                </div>
              ))}
          </div>
        </div>
      </div>
    </div>
  );
};

export default Todo;

This is todo card


const TodoCard = ({ title, id, delid }) => {
  const [isEditing, setIsEditing] = useState(false);
  const [Inputs, setInputs] = useState({
    title: "",
  });

  useEffect(() => {
    setInputs({ title });
  }, [title]);

  const change = (e) => {
    const { name, value } = e.target;
    setInputs({ ...Inputs, [name]: value });
  };

  const handleEdit = () => {
    setIsEditing(true);
    console.log(title);
  };

  const handleUpdate = async () => {
    setIsEditing(false);
    await axios
      .put(`http://localhost:1000/api/v2/updateTask/${id}`, Inputs)
      .then((response) => {
        console.log(response);
        toast.success("Task Added");
        // onUpdate();
      });
    console.log(Inputs);
    console.log("title is  : ", title);
  };

  return (
    <div>
      <div className="task-cont">
        {isEditing ? (
          <input
            name="title"
            type="text"
            value={Inputs.title}
            onChange={change}
          />
        ) : (
          <div className="tasks">{title}</div>
        )}
        <div className="task-icons">
          {isEditing ? (
            <ImCheckmark className="icons" onClick={handleUpdate} />
          ) : (
            <>
              <MdDelete className="icons" onClick={() => delid(id)} />
              <MdModeEditOutline
                className="icons"
                onClick={handleEdit}
                name="title"
              />
            </>
          )}
        </div>
      </div>
    </div>
  );
};

export default TodoCard;

I tried to add and update task - it works on backend but not seems in window , without refresh .

1

There are 1 best solutions below

0
Drew Reese On

There is nothing in the code you provided that updates the local Array state array when any specific todo is updated.

I suggest the following refactor to:

  • Update the todos client-side when the PUT request is made to update a specific todo
  • Move handleUpdate callback into the parent Todo component so it can update the todos state array. Pass as a callback down to the TodoCard component.
  • Update TodoCard to call the passed onUpdate prop and await it to resolve.
  • Make the code a bit more readable
const Todo = () => {
  const navigate = useNavigate();

  const [todos, setTodos] = useState([]);
  const [title, setTitle] = useState("");

  const id = sessionStorage.getItem("id");
  const username = localStorage.getItem("username");

  useEffect(() => {
    const fetchTasks = async () => {
      try {
        const { data } = await axios.get(
          `http://localhost:1000/api/v2/getTasks/${id}`
        );
        setArray(data.list);
      } catch (error) {
        console.error("Failed to fetch tasks:", error);
      }
    };

    if (id === null) {
      navigate("/login");
    } else {
      fetchTasks();
    }
  }, [id, navigate]);

  const change = (e) => {
    setTitle(e.target.value));
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!title) {
      toast.error("Enter Task");
    } else {
      if (id) {
        const response = await axios.post(
          "http://localhost:1000/api/v2/addTask",
          { title, id }
        );
        toast.success("Task Added");
      } else {
        setTodos(todos => todos.concat({ title }));
        toast.success("Task Added");
        toast.error("No id found");
      }
      setTitle("");
    }
  };

  const handleDelete = React.useCallback(async (cardId) => {
    try {
      const response = await axios.delete(
        `http://localhost:1000/api/v2/deleteTask/${cardId}`,
        { data: { id } }
      );
      if (response.status === 200) {
        // Filter out the deleted task from the Array state
        setTodos(todos => todos.filter((item) => item._id !== cardId));
        toast.success("Task Deleted");
      }
    } catch (error) {
      console.error(error);
      toast.error("Failed to delete task");
    }
  }, [id]);

  const handleUpdate = React.useCallback(async (id, title) => {
    const { data } = await axios.put(
      `http://localhost:1000/api/v2/updateTask/${id}`,
      { title }
    );
    toast.success("Task Added");
    setTodos(todos => todos.map(
      todo => todo._id === id
        ? { ...todo, title }
        : todo
    ));
    return data;
  }, []);
 
  return (
    <div className="todo">
      <nav>
        <h1>Welcome {username}</h1>
        <button className="btnlogout" onClick={() => navigate("/")}>
          Logout
        </button>
      </nav>

      <div className="todo-heading">
        <h1>To-Do List</h1>
      </div>

      <div className="todo-container">
        <ToastContainer />
        <div className="todo-form">
          <form onSubmit={handleSubmit} style={{ display: "flex" }}>
            <input
              className="addtodoinput"
              type="text"
              placeholder="Enter task"
              name="title"
              onChange={change}
              value={title}
            />
            <button className="add" type="submit">
              Add Task
            </button>
          </form>
        </div>
        <div className="todo-body">
          <div>
            {todos.map((todo) => (
              <div key={todo._id} className="todo-container">
                <TodoCard
                  todo={todo}
                  onDelete={handleDelete}
                  onUpdate={handleUpdate}
                />
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
};

export default Todo;
const TodoCard = ({ todo, onDelete, onUpdate }) => {
  const [isEditing, setIsEditing] = useState(false);
  const [title, setTitle] = useState(todo.title);

  useEffect(() => {
    setTitle(todo.title);
  }, [todo]);

  const change = (e) => {
    setTitle(e.target.value);
  };

  const handleEdit = () => {
    setIsEditing(true);
  };

  const handleUpdate = async () => {
    setIsEditing(false);
    try {
      await onUpdate(todo._id, title);
    } catch(error) {
      // handle/ignore error?
    } finally {
      setIsEditing(false);
    }
  };

  return (
    <div>
      <div className="task-cont">
        {isEditing ? (
          <input
            name="title"
            type="text"
            value={title}
            onChange={change}
          />
        ) : (
          <div className="tasks">{title}</div>
        )}
        <div className="task-icons">
          {isEditing ? (
            <ImCheckmark className="icons" onClick={handleUpdate} />
          ) : (
            <>
              <MdDelete className="icons" onClick={() => onDelete(todo._id)} />
              <MdModeEditOutline
                className="icons"
                onClick={handleEdit}
                name="title"
              />
            </>
          )}
        </div>
      </div>
    </div>
  );
};