Как реализовать функционал undo и redo для списка постов?
Что проверяют
Умеете ли вы спроектировать историю изменений: какую структуру данных взять, что хранить в каждом шаге и как это стыкуется с сервером. Список постов намекает, что данные живут не только в браузере. Следом спрашивают, как отменить удаление поста, который уже удалён на сервере.
Как отвечать
Два подхода, и стоит назвать оба:
- снимки состояния: модель
past,present,future. Новое действие кладётpresentвpastи очищаетfuture, undo переносит последний снимок изpastвpresent, redo делает обратное. Это классический рецепт из документации Redux и основа библиотекиredux-undo. С иммутабельными обновлениями снимки дешёвые: неизменённые посты переиспользуются по ссылке; - команды: каждое действие хранит, как его выполнить и как отменить, например
{ type: 'remove', post, index }и обратноеinsert. Памяти меньше, а главное, отмену можно отправить на сервер как обычную операцию. Immer послеenablePatches()умеет выдавать патчи и обратные патчи черезproduceWithPatches, это готовая основа для такого варианта.
Дальше детали, которые показывают опыт:
- лимит истории: хранить, скажем, последние 50 шагов, старые отбрасывать;
- группировка: перетаскивание или пакетное удаление это один шаг, а не двадцать;
- синхронизация: удаление на сервере лучше делать мягким или отложенным, тогда undo превращается в запрос на восстановление. Если список правят другие люди, отмена применяется к текущей версии и может конфликтовать;
- горячие клавиши:
Ctrl+ZиCmd+Z, повтор черезCtrl+Shift+ZилиCtrl+Y. Внутри полей ввода их не перехватывайте, там работает нативная отмена браузера.
Где спотыкаются
Хранят в стеке поверхностные копии [...posts], а сами посты мутируют на месте. Все снимки ссылаются на одни и те же объекты, и отмена правки текста ничего не отменяет.
Забывают очищать future при новом действии. После undo и нового изменения redo возвращает ветку истории, которой уже не существует.
Отменяют только локально. Пользователь нажал undo, пост вернулся на экран, а на сервере его нет, и после обновления страницы он снова пропадает.