Обратите внимание на: мутацию state вместо иммутабельного обновления, key={i} по индексу, индекс как идентификатор в toggle, лишние пересчёты и пересоздания функций.
Разбор ошибок
Что сломано
Мутация state в addTodo.items.push(...) меняет тот же массив, а setItems(items) получает ту же ссылку — React не увидит изменения и не перерендерит.
Мутация state в toggle.items[i].done = ... — та же проблема со ссылкой.
key={i} по индексу. При фильтрации/удалении индексы сдвигаются, React путает элементы — теряется состояние и ломается анимация. Нужен стабильный todo.id.
toggle(i) по индексу отфильтрованного массива.i — индекс в visible, а мутируется items[i] — это другой элемент. Переключать нужно по id.
stats() вызывается дважды за рендер и пересоздаётся каждый раз — лишняя работа. Выносится в useMemo.
addTodo/toggle пересоздаются на каждый рендер — если их прокидывать в мемоизированные дочерние компоненты, те будут перерендериваться зря. Оборачиваются в useCallback.