ta539tg70
ta539tg70

Reputation: 317

How to refresh the list after delete in Next.js to-do app

I'm making a simple to-do app using Next.js + TypeScript + Axios and trying to get the delete button to work.
My delete button deletes the selected task as expected, but I want it to also refresh my tasks list so that I can see the updated list.

Any suggestions would be appreciated. Thanks in advance!

// pages/index.tsx

import DeleteIcon from "@mui/icons-material/Delete";
import EditIcon from "@mui/icons-material/Edit";
import FactCheckIcon from "@mui/icons-material/FactCheck";
import {
  List,
  ListItem,
  ListItemAvatar,
  Avatar,
  ListItemText,
  Container,
  Typography,
  IconButton,
  Stack,
} from "@mui/material";

import { deleteTask } from "../modules/apiClient/tasks/deleteTask";
import { useFetchTasks } from "../modules/hooks/useFetchTasks";

interface OperationButtonProps {
  taskId: number;
}

const OperationButtons: React.VFC<OperationButtonProps> = (props) => {
  const handleDelete = async () => {
    try {
      await deleteTask(props.taskId); // calls the API and deletes the task fine
      // I have to do something here or somewhere to refresh the tasks list but can't figure out how.
    } catch (e) {
      console.log(e);
    }
  };

  return (
    <Stack direction="row">
      <IconButton aria-label="edit">
        <EditIcon />
      </IconButton>
      <IconButton edge="end" aria-label="delete" onClick={handleDelete}>
        <DeleteIcon />
      </IconButton>
    </Stack>
  );
};

const IndexPage = () => {
  const { tasks } = useFetchTasks();

  return (
    <>
      <Typography variant="h3" align="center" marginTop={3}>
        TODO LIST
      </Typography>
      {tasks && (
        <Container maxWidth="sm">
          <List sx={{ width: "100%" }}>
            {tasks.tasks.map((task) => (
              <ListItem
                key={task.id}
                secondaryAction={<OperationButtons taskId={task.id} />}
              >
                <ListItemAvatar>
                  <Avatar>
                    <FactCheckIcon />
                  </Avatar>
                </ListItemAvatar>
                <ListItemText
                  primary={task.description}
                  secondary={task.created_at}
                />
              </ListItem>
            ))}
          </List>
        </Container>
      )}
    </>
  );
};

export default IndexPage;
// modules/hooks/useFetchTasks.ts

import { useState, useEffect, useCallback } from "react";

import { getTasks, TasksResponse } from "../apiClient/tasks/getTasks";

export const useFetchTasks = () => {
  const [tasks, setTasks] = useState<TasksResponse | null>(null);

  const fetchTasks = useCallback(async () => {
    try {
      const { data } = await getTasks();
      setTasks(data);
    } catch (e) {
      console.log(e);
    }
  }, []);

  useEffect(() => {
    fetchTasks();
  }, []);

  return {
    tasks,
    fetchTasks,
  };
};

Upvotes: 0

Views: 2464

Answers (1)

iamhuynq
iamhuynq

Reputation: 5529

You can return setTasks your useFetchTasks and pass it to your button, so you can update your list after called API

const OperationButtons: React.VFC<OperationButtonProps> = (props) => {
    const handleDelete = async () => {
        try {
            await deleteTask(props.taskId);
            props.setTasks(prevTasks => prevTasks.tasks.filter(task => task.id !== props.taskId))
        } catch (e) {
            console.log(e);
        }
    };

    return (
        <Stack direction="row">
            <IconButton aria-label="edit">
                <EditIcon />
            </IconButton>
            <IconButton edge="end" aria-label="delete" onClick={handleDelete}>
                <DeleteIcon />
            </IconButton>
        </Stack>
    );
};

const IndexPage = () => {
    const { tasks, setTasks } = useFetchTasks();

    return (
        <>
            <Typography variant="h3" align="center" marginTop={3}>
                TODO LIST
            </Typography>
            {tasks && (
                <Container maxWidth="sm">
                    <List sx={{ width: '100%' }}>
                        {tasks.tasks.map((task) => (
                            <ListItem
                                key={task.id}
                                secondaryAction={<OperationButtons taskId={task.id} setTasks={setTasks} />}
                            >
                                <ListItemAvatar>
                                    <Avatar>
                                        <FactCheckIcon />
                                    </Avatar>
                                </ListItemAvatar>
                                <ListItemText
                                    primary={task.description}
                                    secondary={task.created_at}
                                />
                            </ListItem>
                        ))}
                    </List>
                </Container>
            )}
        </>
    );
};

export const useFetchTasks = () => {
    const [tasks, setTasks] = (useState < TasksResponse) | (null > null);

    const fetchTasks = useCallback(async () => {
        try {
            const { data } = await getTasks();
            setTasks(data);
        } catch (e) {
            console.log(e);
        }
    }, []);

    useEffect(() => {
        fetchTasks();
    }, []);

    return {
        tasks,
        fetchTasks,
        setTasks
    };
};

export default IndexPage;

Upvotes: 1

Related Questions