skip to Main Content

I use the following hook in a page.tsx file and then pass the data down to a table. When I click on a task, i have the following function

page.tsx
"use client"

const { allOpenTasks, getOpenTasks } = useTasks();
  useEffect(() => {
    const fetchTasks = async () => {
      await getOpenTasks(user.id);
    };
    fetchTasks();
  }, []);

useTasks.tsx

export const useTasks = () => {
  const [allOpenTasks, setAllOpenTasks] = useState<any>([]);

  const getOpenTasks = async (userId: any) => {
    try {
      const { data, error } = await supabase
        .from("task")
        .select(
          `
          id,
          is_complete
      `
        )
        .eq("owner_id", userId);

      if (error) {
        throw error;
      }
      if (data) {
        setAllOpenTasks(data);
      }
    } catch (error: any) {
      console.log(error.message);
    }
  };

  const setTaskCompleted = function (taskId: any) {
    const updatedTasks = allOpenTasks.map((task: any) =>
      task.id === taskId ? { ...task, is_complete: !task.is_complete } : task
    );
    setAllOpenTasks([...updatedTasks]);
  };

  return {
    allOpenTasks,
    getOpenTasks,
    setTaskCompleted
  };
};

Table.tsx
"use client"

  const { setTaskCompleted } = useTasks();
  const clickCompleteTask = async (taskId: any) => {
    try {
      const { error } = await supabase
        .from("task")
        .update({
          is_complete: true,
        })
        .eq("id", taskId);
      if (error) throw new Error();
      setTaskCompleted(taskId);
    } catch (err) {
      console.log(err);
    }
  };

I have tried the following which works.
Instead of keeping the tasks state inside of the hook, i can maintain it in the page.tsx file and only use the hook to fetch the data from supabase.

I think it looks more clean if its inside the hook however it does not re-render on state change for a task object in page.tsx (when its set to completed)

I suspect its because when setTaskCompleted is initialized in the start, the allOpenTasks is empty since supabase hasn’t set that yet.

If there is a better pattern i’d also be open to this.

Thanks!

2

Answers


  1. Chosen as BEST ANSWER

    It was actually how useHooks work. Importing useTasks in two different components led to two separate instances. Instead needed to pass setTaskCompleted down as a prop.


  2. could you try

    const setTaskCompleted = function (taskId: any) {
      if (allOpenTasks.length > 0) {
        const updatedTasks = [...allOpenTasks].map((task: any) =>
          task.id === taskId ? { ...task, is_complete: !task.complete } : task
        );
        setAllOpenTasks(updatedTasks);
      } else {
        console.log("Tasks not loaded yet, cannot update completion status.");
      }
    };
    

    To address this and ensure a re-render when the data within allOpenTasks changes.

    I think it have 2 problems,

    • if (allOpenTasks.length > 0) is data is empty if supabase return empty, ensure data is fetched before update i have condition for this.
    • [...allOpenTasks].map To address this and ensure a re-render when the data within allOpenTasks changes.

    anyway, i think you code is perfect overall

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search