Untitled

 avatar
unknown
plain_text
4 years ago
3.0 kB
5
Indexable
import React, { useEffect, useState, useCallback, useMemo } from "react";

type TodoItem = {
  id: number;
  title: string;
  isDone: boolean;
};

type Props = {
  todos: TodoItem[];
};

enum FilterType {
  All = "all",
  Undone = "undone",
}

const Todos: React.FC<Props> = ({ todos: defaultTodos }) => {
  const [todos, setTodos] = useState<TodoItem[]>(defaultTodos);
  const [filter, setFilter] = useState<FilterType>(FilterType.All);

  const handleAddTodo = useCallback(() => {
    const title = prompt("What to do?");

    if (!title) return;

    const newTodo = { id: +Date.now(), title, isDone: false };

    setTodos((prevTodos) => [...prevTodos, newTodo]);
  }, []);

  const makeHandleChangeTodoStatus = useCallback(
    (id: number) => () => {
      setTodos((prevTodos) => changeTodoStatus(prevTodos, id));
    },
    []
  );

  const makeHandleDeleteTodo = useCallback(
    (id: number) => () => {
      setTodos((prevTodos) => removeFromArrById(prevTodos, id));
    },
    []
  );

  const handleChangeFilter = useCallback(() => {
    setFilter((prevFilter) =>
      prevFilter === FilterType.All ? FilterType.Undone : FilterType.All
    );
  }, []);

  useEffect(() => {
    setTodos(defaultTodos);
    setFilter(FilterType.All);
  }, [defaultTodos]);

  const visibleTodos =
    filter === FilterType.Undone
      ? todos.filter(({ isDone }) => !isDone)
      : todos;

  const doneCount = useMemo(
    () => todos.filter(({ isDone }) => isDone).length,
    [todos]
  );

  const filterMessage = `Show ${
    filter === FilterType.All ? "undone" : "all"
  } todos`;

  return (
    <div>
      <p>{`Done: ${doneCount} / ${todos.length}`}</p>
      <ul>
        {visibleTodos.map(({ id, title, isDone }) => (
          <div key={id}>
            <p>
              {isDone && "✅ "}
              {title}
            </p>
            <button type="button" onClick={makeHandleChangeTodoStatus(id)}>
              {isDone ? "Undone" : "Done"}
            </button>
            <button type="button" onClick={makeHandleDeleteTodo(id)}>
              Delete
            </button>
          </div>
        ))}
      </ul>

      <button type="button" onClick={handleAddTodo}>
        Add
      </button>
      <button type="button" onClick={handleChangeFilter}>
        {filterMessage}
      </button>

      <ExpensiveTree />
    </div>
  );
};

function changeTodoStatus(todos: TodoItem[], id: number) {
  return todos.map((todo) =>
    todo.id === id ? { ...todo, isDone: !todo.isDone } : todo
  );
}

function removeFromArrById<T extends { id: number }>(arr: T[], id: number) {
  return arr.filter((item) => item.id !== id);
}

function ExpensiveTree() {
  const now = performance.now();

  while (performance.now() - now < 1000) {
    // Artificial delay -- do nothing for 1000ms
  }

  return null;
}

export default React.memo(Todos);
Editor is loading...