0
votes

i'm trying to work on a todo app with the option of editing. the goal is to click on the edit button and that'll open an input field, type the new editted text and then have two choices , save the changes or not.

i've managed to write the code for it to open the input field, and to be able to click on the button to not save changes ,but what happens is that it opens the input field for all of the todos ,and whenever i try to update the value of the specific todo i get the error "todos.map is not a function".

Here's the TodoList.js

import Todo from "./Todo";
import AddTodo from "./AddTodo";
import { v4 as uuidv4 } from "uuid";

const TodoList = () => {
  //Handlers Add/Remove/RemoveAll/Edit
  const addTodoHandler = (input) => {
    setTodos([
      ...todos,
      {
        name: input,
        id: uuidv4(),
      },
    ]);
  };

  const changeEditMode = (id) => {
    setEditMode(!editMode);
    console.log(id);
  };

  const removeTodosHandler = () => {
    if (window.confirm("Are you sure you want to delete everything?")) {
      setTodos([]);
    }
  };

  const removeTodoHandler = (id) => {
    setTodos(todos.filter((todo) => todo.id !== id));
  };

  const updateValue = (id) => {
    inputRef.current.focus();
    setEditMode(!editMode);
    setTodos({ name: inputRef.current.value });
  };

  //Todo list states.
  const inputRef = useRef("");
  const [todos, setTodos] = useState([]);
  const [editMode, setEditMode] = useState(false);

  return (
    <div>
      <div>
        {todos.map((todo) => {
          return (
            <div>
              {editMode ? (
                <div>
                  {" "}
                  <input
                    type="text"
                    defaultValue={todo.name}
                    ref={inputRef}
                  ></input>
                  <button onClick={(e) => updateValue(todo.id)}>ok</button>
                  <button onClick={(e) => setEditMode(!editMode)}>x</button>
                </div>
              ) : (
                <div></div>
              )}
              <Todo name={todo.name} key={todo.id} />
              <button onClick={() => removeTodoHandler(todo.id)}>X</button>
              <button onClick={(e) => changeEditMode(todo.id)}>Edit</button>
            </div>
          );
        })}
      </div>
      <AddTodo
        handleAddTodo={addTodoHandler}
        removeTodosHandler={removeTodosHandler}
        revemoveTodoHandler={removeTodoHandler}
      />
    </div>
  );
};

export default TodoList;

and here's the Todo.js


const Todo = ({ name }) => {
  return (
    <div>
      <div>{name}</div>
    </div>
  );
};

export default Todo;

Any help appreciated!

2

2 Answers

0
votes

Your updateValue function is setting your todos to an object. So it gives you that error because you can't use map method for objects.

0
votes

In your updateValue method you are setting your todoList to and object.

  const updateValue = (id) => {
    inputRef.current.focus();
    setEditMode(!editMode);
    setTodos({ name: inputRef.current.value });
  };

But what you have to do is first find out the item with the id and then update the name property of that object and then again set the new array to setTodos setter.

Like this:

    const clonedTodos = todos;
    const todoIndex = clonedTodos.findIndex((todo) => todo.id === id);
    const updatedTodo = {
      ...clonedTodos[todoIndex],
      name: inputRef.current.value,
    };
    const updatedTodos = [...clonedTodos];
    updatedTodos[todoIndex] = updatedTodo;
    setTodos(updatedTodos);