Sunny-117 / js-challenges

✨✨✨ Challenge your JavaScript programming limits step by step
https://juejin.cn/column/7244788137410560055
1.92k stars 232 forks source link

实现todos #173

Open Sunny-117 opened 1 year ago

floatGray commented 3 months ago

src/TodoList/Todo.jsx

import { useState } from 'react';

export default function Todo({ todo, handleUpdateTodo, handleDeleteTodo }) {
  const [editing, setEditing] = useState(false);

  const handleCheckboxClick = () => {
    handleUpdateTodo({
      ...todo,
      completed: !todo.completed,
    });
  };

  const handleEditClick = () => {
    setEditing(!editing);
  };

  const handleEditTodo = (e) => {
    handleUpdateTodo({
      ...todo,
      label: e.target.value,
    });
  };

  const handleDeleteClick = () => handleDeleteTodo(todo.id);

  return (
    <li style={{ textAlign: 'left' }}>
      <label htmlFor={todo.id}>
        <input
          type="checkbox"
          id={todo.id}
          checked={todo.completed}
          onChange={handleCheckboxClick}
        />

        {editing === true ? (
          <input type="text" value={todo.label} onChange={handleEditTodo} />
        ) : (
          <span>{todo.label}</span>
        )}
      </label>

      <button
        disabled={editing && !todo.label.length}
        onClick={handleEditClick}
      >
        {editing ? 'Save' : 'Edit'}
      </button>
      {!editing && <button onClick={handleDeleteClick}>Delete</button>}
    </li>
  );
}

src/TodoList/TodoComposer.jsx

import { useState } from 'react';

function createTodo(label) {
  return {
    id: Math.floor(Math.random() * 10000),
    label,
    completed: false,
  };
}

export default function TodoComposer({ handleAddTodo }) {
  const [label, setLabel] = useState('');

  const handleUpdateLabel = (e) => setLabel(e.target.value);

  const handleAddTodoClick = () => {
    const todo = createTodo(label);
    handleAddTodo(todo);
    setLabel('');
  };

  return (
    <li>
      <input
        type="text"
        value={label}
        placeholder="New todo"
        onChange={handleUpdateLabel}
      />
      <button disabled={label.length === 0} onClick={handleAddTodoClick}>
        Add Todo
      </button>
    </li>
  );
}

src/TodoList/TodoList.jsx

import { useState } from 'react';
import TodoComposer from './TodoComposer';
import Todo from './Todo';

export default function TodoList() {
  const [todos, setTodos] = useState([
    { id: 1, label: 'Learn React', completed: false },
    { id: 2, label: 'Learn Next.js', completed: false },
    { id: 3, label: 'Learn React Query', completed: false },
  ]);

  const handleUpdateTodo = (updatedTodo) => {
    const newTodos = todos.map((todo) => {
      return todo.id === updatedTodo.id ? updatedTodo : todo;
    });
    setTodos(newTodos);
  };

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

  const handleAddTodo = (newTodo) => {
    const newTodos = [...todos, newTodo];
    setTodos(newTodos);
  };

  return (
    <ul>
      <TodoComposer handleAddTodo={handleAddTodo} />
      {todos.map((todo) => {
        return (
          <Todo
            key={todo.id}
            todo={todo}
            handleUpdateTodo={handleUpdateTodo}
            handleDeleteTodo={handleDeleteTodo}
          />
        );
      })}
    </ul>
  );
}