'Encountring very fundamental problem with react, why item.id in my case is increasing by +2?

hi i have just started learning react and facing problems in understanding fundamentals , here i am creating basic todo app question is why item-id in seq of 1,3,5,7.... as show in image attached How do i slove this ?

import { useState } from "react";
import "./App.css";

let counter = 0;

function App() {
 const [text, setText] = useState("");
 const [todos, setTodos] = useState([]);

const createTodo = (e) => {
  e.preventDefault();
  setText('')
  setTodos((oldTodos) => [...oldTodos, { todo: text, id: counter++ }]);
};

 return (
   <div className="App">

     <form onSubmit={createTodo}>
     <input
      type="text"
      value={text}
      onChange={(e) => setText(e.target.value)}
     ></input>
     <button type="submit">Add Todo</button>
     </form>

   <ul>
    {todos.map((item) => {
      return (
        <li key={item.id}>
          {item.todo}-{item.id}
        </li>
      );
    })}
  </ul>

 </div>
 );
}

export default App;

browser output [output in browser1

Any better approach to slove issue??



Solution 1:[1]

try this code instead

 const [text, setText] = useState("");
 const [todos, setTodos] = useState([]);
 const [count , setCount] = useState(0);

const createTodo = (e) => {
  e.preventDefault();
  setCount(count++)
  setText('')
  setTodos((oldTodos) => [...oldTodos, { todo: text, id: count }]);
};

Solution 2:[2]

Answer: getting rid of StrictMode mode, you will produce the desired output.

But why?

With StrictMode:

  const [text, setText] = useState("");
  const [todos, setTodos] = useState([]);

  console.log("Renders!"); // Add logging to see what happens
  console.log(todos);

On page load, console shows:

Renders!
[]
Renders!
[]

the page reders twice, which is expected, and now we submit a string value one, and the console shows:

Renders! // First render
[{todo: "one", id: 0}]

Renders! // Second render
[{todo: "one", id: 1}] // notice id value

From output, we can see the id of first todo item is already at 1, and it should've been zero, the starting value of counter. That's because the setter invoked twice, as the page renders twice:

setTodos((oldTodos) => [...oldTodos, { todo: text, id: counter++ }]);

Without StrictMode:

Renders!
[{todo: "one", id: 0}]

Only renders once, and id is the starting point 0.

Sandbox here and for learning material recommend react beta docs

Solution 3:[3]

counter++ means you are re-assigning the value of the counter variable. counter = counter +1, counter += 1 are alternatives for it. So what you can try is you can first change the value of the variable and then assign it to the id property.

let counter = 0;

function App(){
    const [text, setText] = useState("");
    const [todos, setTodos] = useState([]);

    const createTodo = (e) => {
        e.preventDefault();
        setText('');
        counter++; // or counter += value or counter = counter + value;
        setTodos((oldTodos) => [...oldTodos, { todo: text, id: counter }]);
    };

}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Eisa Rezaei
Solution 2
Solution 3 Shakya Peiris