Разработка на javascript иногда становится похожа на работу детектива. Как понять чужой код? Хорошо, если разработчик обладает тонким искусством называть переменные так, чтобы другие поняли суть. А как быть, если члены команды все таки не всегда способны понять замысел своего коллеги? Как понять, что приходит в аргумент какой-либо функции?  Предположим, что аргумент функции называется errors. Вероятно в errors находится массив. Скорее всего строк? Ну то, что массив это понятно. Ведь далее проверяется его длина. Но свойство length есть и у строки. Похоже, чтобы точно разобраться, необходимо поставить breakpoint и запустить скрипт. Затем полностью пройти по сценарию на UI (например нам нужен финальный шаг формы). Теперь в devtools видно, что errors — это объект с набором определенных полей, среди которых и поле length. Подобная неоднозначность при разборе javascript кода приводит к пустой трате времени разработчика. Неплохим решением в данном случае может стать typescript (далее ts). Можно использовать его в следующем проекте, а еще лучше сделать поддержку ts в существующем. После этого время на понимание чужого кода сократится значительно. Ведь, чтобы понять структуру любых данных достаточно одного клика. Можно сконцентрироваться на логике работы с данными и в любой момент времени знать, что вы однозначно понимаете работу кода. Следует отметить некоторые достоинства ts. Его широко используют в различных фреймворках и он тесно связан с javascript. Развитие ts обусловливается потребностями frontend разработчиков. В данной статье представлена разработка todo приложения, но только краткое описание интересных моментов. Полный код можно найти тут. Я использовал react, typescript и mobx. Mobx — гибкое средство для управления состоянием приложения. Mobx лаконичен. Он позволяет работать с состоянием компонентов react в синхронном стиле. Нет проблем типа: В данном случае выведется старое значение state.name. Кроме того, mobx удобен и не мешает работать с типами ts. Можно описывать state в виде отдельных классов или прямо внутри react компонента. Для простоты все компоненты помещены в папку components. В папке компонента определен класс с описанием состояния, связанного логически с отображением и работой с компонента. В папке TodoItem находится файл с react компонентом TodoItem.tsx, файл со стилями TodoItem.module.scss и файл состояния TodoItemState.ts. В TodoItemState.ts описаны поля для хранения данных, способы доступа к ним и правила их изменения. Круг возможностей очень велик благодаря ООП и ts. Часть данных может быть приватной, часть открыта только для чтения и прочее. С помощью декоратора @o указаны observable поля. На их изменения реагируют react компоненты. Декораторы @a (action) используются в методах для изменения состояния. В TodoItem.tsx в props передается всего два свойства. В mobx оптимально для общей производительности приложения передавать сложные структуры данных в props react компонента. Поскольку мы используем ts, то можно точно указать тип принимаемого компонентом объекта. В интерфейсе ITodoItemProps описано todo свойство типа TodoItemState. Таким образом внутри react компонента мы обеспечены данными для отображения и методами их изменения. Причем, ограничения на изменение данных можно описать как в state классе, так и в методах react компонента, в зависимости от поставленных задач.  Компонент TodoList похож на TodoItem. В TodoListState.ts можно заметить геттеры с декоратором @c (@computed). Это обычные геттеры классов, только их значения мемоизируются и пересчитываются при изменении их зависимостей. Computed по назначению похож на селекторы в redux. Удобно, что не нужно, подобно React.memo или reselect, явно передавать список зависимостей. React компоненты реагируют на изменение computed также как и на изменение observable. Интересной особенностью является то, что перерасчет значения не происходит, если в данный момент computed не участвует в рендере (что экономит ресурсы). Поэтому, несмотря на сохранение постоянных значений зависимостей, computed может пересчитаться (существует способ явно указать mobx, что необходимо сохранять значение computed). Доступ к списку todo открыт только через computed поле, где, в зависимости от режима просмотра, возвращается необходимый отфильтрованный набор данных (завершенные, активные или все todo). В зависимостях todo указаны computed поля completedTodos, activeTodos и приватное observable поле _todos. Рассмотрим главный компонент App. В нем рендерятся форма для добавления новых todo и список todo. Тут же создается экземпляр главного стейта AppSate. В поле appState находится экземпляр класса TodoListState для отображения компонента TodoList и метод добавления новых todo, который передается в компонент AddTodo. Компонент AddTodo имеет изолированный стейт. К нему нет доступа из общего стейта. Единственная связь с appState осуществляется через метод appState.addTodo при submit формы.
Для стейта компонента AddTodo используется библиотека formstate, которая отлично дружит с ts и mobx. Formstate позволяет удобно работать с формами, осуществлять валидацию форм и прочее. Форма имеет только одно обязательное поле name. В целом, нет смысла описывать полностью поведение всех компонентов. Полный код приведен тут. В данной статье приведена попытка автора писать простой, гибкий и структурированный код, который легко поддерживать. React делит UI на компоненты. В компонентах описаны классы стейтов (можно отдельно тестировать каждый класс). Экземпляры стейтов создаются либо в самом компоненте, либо уровнем выше, в зависимости от задач. Достаточно удобно, что можно указывать типы полей класса и типы свойств компонентов благодаря typescript. Благодаря mobx мы можем, практически незаметно для разработчика, заставить react компоненты реагировать на изменение данных.