BACK
Уроки45. scan и immutable todo list
Уроки · 45

45. scan и immutable todo list

Immutable updates — закон сохранения старого

В UI-приложениях принято обновлять состояние иммутабельно. Это значит: не менять старые объекты и массивы, а создавать новые. Старый объект должен остаться нетронутым.

Зачем это нужно

  • Сравнение по ссылке: Angular OnPush, distinctUntilChanged, React, Signals — все они проверяют «новое ли это?» через ===. Мутация не даст изменения ссылки → UI не обновится.
  • Time travel: иммутабельные снимки можно сохранять и откатывать.
  • Прозрачная отладка: предсказуемо видеть, что изменилось.

Иммутабельные приёмы

// добавить в массив:
[...old, newItem]

// обновить элемент массива по условию:
old.map(item => item.id === id ? { ...item, done: !item.done } : item)

// обновить поле объекта:
{ ...old, name: 'new' }

Что нужно сделать

  1. Для action.type === 'add' верните [...todos, { id: action.id, title: action.title, done: false }].
  2. Для action.type === 'toggle' верните todos.map(...): для нужного id — новый объект { ...todo, done: !todo.done }, иначе сам todo.
  3. В остальных случаях верните todos.
  4. Никаких push, splice, todo.done = ... — это мутации!
Решение spoiler · click to reveal
const { from, scan } = Rx;

const actions$ = from([
  { type: 'add', id: 1, title: 'learn RxJS' },
  { type: 'add', id: 2, title: 'build app' },
  { type: 'toggle', id: 1 },
]);

function formatTodos(todos) {
  return todos
    .map(todo => todo.title + '[' + (todo.done ? 'x' : ' ') + ']')
    .join(' | ');
}

const todos$ = actions$.pipe(
  scan((todos, action) => {
    if (action.type === 'add') {
      return [
        ...todos,
        { id: action.id, title: action.title, done: false },
      ];
    }

    if (action.type === 'toggle') {
      return todos.map(todo => {
        if (todo.id !== action.id) {
          return todo;
        }

        return { ...todo, done: !todo.done };
      });
    }

    return todos;
  }, [])
);

todos$.subscribe(todos => console.log('Todos: ' + formatTodos(todos)));
script.ts // TypeScript
CONSOLE · Console Output
Нажмите на запуск, чтобы увидеть результат...