0

I am creating to-do app in react and for the id of task i am using generator function. But This generator function is giving value 0 everytime and not incrementing the value.I think the reason for issue is useCallback() hook but i am not sure what can be the solution.How to solve the issue?Here i am providing the code :

import DateAndDay, { date } from "../DateAndDay/DateAndDay";
import TaskList, { TaskProps } from "../TaskList/TaskList";
import "./ToDo.css";
import Input from "../Input/Input";
import { ChangeEvent, useCallback, useEffect, useState } from "react";

function ToDo() {
  const [inputShow, setInputShow] = useState(false);
  const [valid, setValid] = useState(false);
  const [enteredTask, setEnteredTask] = useState("");
  const [touched, setTouched] = useState(false);
  const [tasks, setTasks] = useState<TaskProps[]>(() => {
    let list = localStorage.getItem("tasks");
    let newdate = String(date);
    const setdate = localStorage.getItem("setdate");
    if (newdate !== setdate) {
      localStorage.removeItem("tasks");
    }
    if (list) {
      return JSON.parse(list);
    } else {
      return [];
    }
  });

  const activeHandler = (id: number) => {
    const index = tasks.findIndex((task) => task.id === id);
    const updatedTasks = [...tasks];
    updatedTasks[index].complete = !updatedTasks[index].complete;
    setTasks(updatedTasks);
  };

  const clickHandler = () => {
    setInputShow((prev) => !prev);
  };

  const input = inputShow && (
    <Input
      checkValidity={checkValidity}
      enteredTask={enteredTask}
      valid={valid}
      touched={touched}
    />
  );

  const btn = !inputShow && (
    <button className="add-btn" onClick={clickHandler}>
      +
    </button>
  );

  function checkValidity(e: ChangeEvent<HTMLInputElement>) {
    setEnteredTask(e.target.value);
  }

  function* idGenerator() {
    let i = 0;
    while (true) {
      yield i++;
    }
  }
  let id = idGenerator();

  const submitHandler = useCallback(
    (event: KeyboardEvent) => {
      event.preventDefault();
      setTouched(true);
      if (enteredTask === "") {
        setValid(false);
      } else {
        setValid(true);
        const newtitle = enteredTask;
        const newComplete = false;
        const obj = {
          id: Number(id.next().value),
          title: newtitle,
          complete: newComplete,
        };
        setTasks([...tasks, obj]);
        localStorage.setItem("setdate", date.toString());
        setEnteredTask("");
      }
    },
    [enteredTask, tasks, id]
  );

  useEffect(() => {
    const handleKey = (event: KeyboardEvent) => {
      if (event.key === "Escape") {
        setInputShow(false);
      }
      if (event.key === "Enter") {
        submitHandler(event);
      }
    };

    document.addEventListener("keydown", handleKey);

    return () => {
      document.removeEventListener("keydown", handleKey);
    };
  }, [submitHandler]);

  useEffect(() => {
    localStorage.setItem("tasks", JSON.stringify(tasks));
  }, [tasks]);

  return (
    <div className="to-do">
      <DateAndDay />
      <TaskList tasks={tasks} activeHandler={activeHandler} />
      {input}
      {btn}
    </div>
  );
}
export default ToDo;

front_dev_j
  • 139
  • 1
  • 12
  • Please show us how you are using `submitHandler`. Are you calling it multiple times per render (per `let id = idGenerator();`)? – Bergi Feb 28 '22 at 11:26
  • You did not memoise the `idGenerator()` call, so `id` is a new generator on every render, and since it's a dependency of your `useCallback` hook that will also change on every render. – Bergi Feb 28 '22 at 11:28
  • i have edited the code, please check it.@Bergi – front_dev_j Feb 28 '22 at 12:16
  • if you have any other way that can generate unique id of type number then it will be also useful for me, because i tried uuid package but it is not giving the type number – front_dev_j Feb 28 '22 at 13:13

2 Answers2

0

useCallBack()'s is used to memorize the result of function sent to it. This result will never change until any variable/function of dependency array changes it's value. So, please check if the dependencies passed are correct or if they are changing in your code or not ( or provide all the code of this file). One of my guess is to add the Valid state as dependency to the array

Asad Ashraf
  • 1,425
  • 8
  • 19
  • i have provided this whole file,please look into that. – front_dev_j Feb 28 '22 at 12:22
  • if you have any other way that can generate unique id of type number then it will be also useful for me, because i tried uuid package but it is not giving the type number – front_dev_j Feb 28 '22 at 13:13
  • Please read this post, it will help you to generate unique id https://stackoverflow.com/questions/29420835/how-to-generate-unique-ids-for-form-labels-in-react Also, accept the answer too, thanks – Asad Ashraf Feb 28 '22 at 13:20
0

It's because you are calling the idGenerator outside of the useCallback, so it is only generated if the Component is re-rendered, in your case... only once.

Transfer it inside useCallback and call it everytime the event is triggered:

// wrap this on a useCallback so it gets memoized
const idGenerator = useCallback(() => {
    let i = 0;
    while (true) {
      yield i++;
    }
  }, []);

  const submitHandler = useCallback(
    (event: KeyboardEvent) => {
      event.preventDefault();
      
      let id = idGenerator();
      // ... rest of logic
    },
    [enteredTask, tasks, idGenerator]
  );

If you're using the generated id outside the event handler, store the id inside a state like so:


const idGenerator = useCallback(() => {
    let i = 0;
    while (true) {
      yield i++;
    }
  }, []);

const [id, setId] = useState(idGenerator());

  const submitHandler = useCallback(
    (event: KeyboardEvent) => {
      event.preventDefault();
      
      let newId = idGenerator();
      setId(newId)
      // ... rest of logic
    },
    [enteredTask, tasks, id, idGenerator]
  );
I am L
  • 4,288
  • 6
  • 32
  • 49
  • since this is generator function.i cannot write it like this :const idGenerator = useCallback(() => { let i = 0; while (true) { yield i++; } }, []); it will give error for yield. – front_dev_j Feb 28 '22 at 12:20
  • @front_dev_j So why not do -> `useCallback(function* () { .....}` – Keith Feb 28 '22 at 12:57
  • using this syntax it shows that "cannot find name idGenerator" – front_dev_j Feb 28 '22 at 13:01
  • if you have any other way that can generate unique id of type number then it will be also useful for me, because i tried uuid package but it is not giving the type number – front_dev_j Feb 28 '22 at 13:13
  • @front_dev_j I was kind of assuming you would put the rest in -> `const idGenerator = useCallback(function *() { .... }, [])` – Keith Feb 28 '22 at 13:15
  • @front_dev_j is your id generator as some special logic or you just want to create random string? I mean does it need to be just number? – I am L Feb 28 '22 at 13:33
  • @front_dev_j is the idGenerator just an increment? coz you can easily do that with just useState. – I am L Feb 28 '22 at 13:37
  • yes, i want only unique numbers – front_dev_j Mar 01 '22 at 03:44