ReactJs. Разбор кода todomvc

Автор:Игорь Тельменко

ReactJs. Разбор кода todomvc

Для того, чтобы лучше понять как выглядит реальное приложение на React я решил разобрать код todomvc. Рассматриваю самую свежую версию кода, которая доступна на момент написания статьи. Если ссылка не работает, то можно скачать архив отсюда. Для чтения статьи очень желателшьно ознакомится хотя бы с базовой информацией и примерами с данной страницы официального сайта.  Ну и лично мне сильно помог кусок данной книги. Надеюсь у нее будет продолжение (пытался найти оригинал - не нашел).

Реализация todomvc для ReactJs состоит из index.html, папки node_modules и папки js. Индексный файл практически пуст. В нем есть только подключение  других файлов и главная секция

Которая совершенно неинформационна. В node_modules содержатся нужные библиотеки, а само сердце приложение находится в папке js. Там у нас имеются следующие файлы

  • app.jsx
  • footer.jsx
  • todoItem.jsx
  • todoModel.js
  • utils.js

Начнем с главного файла - app.jsx. Читать его пожалуй лучше с конца. А там у нас следующее

Как видно здесь идет создание модели, рендеринг TodoApp компонента и подписка компонента на сообытия модели. Но только ведь нет в React двустороннего связывания и моделей тоже нет. И если мы посмотрим в файл todoModel.js, то увидим, что подписка не совсем честная. Callback-функции вызываются в методе модели inform(), который вызывается вручную в каждом методе, где модель меняется свое состояние так, что представлению нужно сделать обновленный рендеринг.

Истользуя тег <TodoApp> мы вызываем метод render() для компонента TodoApp , описаного внутри React.createClass({ ... }).  Сам метод render() так же через теги JSX создает экземпляры компонентов TodoItem (отдельная запись в TODO) и TodoFooter (футер списка, в котором есть кнопки-фильтры и показа число пунктов в списке). Таким образом, зесь демонстрируется, что если у нас описаны компоненты через React.createClass(), то мы можем создавать их экземпляры через одноименные теги JSX и передавать параметры через атрибуты тегов.

Так в TodoItem передается большой список параметров

Вот как мы видим здесь происходит привязка событий onToggle (смена стостояния пункта списка), onEdit (переход в режим редактирования пункта) и onSave (сохранение пункта списка). Но ведь мы знаем, что таких вот высокоуровневых событий нет в React. Верно. События эти опять не совсем честные (как и в случае с моделью). Чтобы удостовериться в этом давайте поищем в файле с описанием комнонета todoItem.jsx вхождение этих строк. Мы увидим, что this.props.onSave(val) вручную вызывается в методе handleSubmit()this.props.onEdit() - в handleEdit(), а this.props.onToggle привязано к событию onChange для чекбокса пункта todo-списка. С последним понятно - это просто алиас onChange. А как дела с остальными? И тут

мы видим, что handleEdit() - это обработчик двойного клика по label-у пункта списка, а здесь

видно, что handleSubmit - обработчик события потери фокуса (onBlur) и события нажатия клавиши Enter (через более сложную связь onKeyDown={this.handleKeyDown}).

По сути мы сейчас разобрали метод render() компонента TodoItem. Это метод будет вызываться каждый раз при отрисовке каждого из пунктов todo-списка. Так же здесь демонстрируется обращение через this.props ко всем данным, что были переданы через атрибуы тега TodoItem.

Отмечу еще интересную привязку обработчиков для событий TodoItem в TodoApp. Вид примерно следующий: onSave={this.save.bind(this, todo)}

Этод код выполняется внутри метода render() нашего TodoApp и привязывает его метод save() обрабтчиком для события компонента TodoItem. При изменении одного пункта списка пересохраняется весь список. Поэтому это оправдано.

Кстати, о сохранении. Посмотрим на метод TodoApp.save()

Здесь вызывается метод save() для модели и затем устанавливается через setState() вывыдим список из состояния редактирования.

Модель же перерабатывает состояние данных, которые описывают состояние списка и вызывае метод inform()

Помните нашши псевдособытия модели?

Вот inform() и сохраняет наши данные в хранилище через базовый объект Utils

и вызывает по очереди все обработчики. Но в данном случае обработчик у нас один.

В TodoApp есть еще один интересный метод

Метод componentDidMount() - стандартный, так же как и render(), и вызывается если рендеринг прошел успешно, и настоящий DOM построен. Может использоваться для самый различных манипуляций. В том числе с сырым DOM. Но в данном случае этот метод используется для привязки роутинга. Как мы помним в React нет роутинга. Так и есть. Здесь используется отдельная библиотека director. Не трудно догадаться как это работает. Для каждого варианта url прописыается свой обработчик, который меняет состояние nowShowing для TodoApp и заставляет его перерисоваться снова. Да, еще один вызов render() после render(). Как в примере с таймером с официального сайта ReactJs.

В целом пример todomvc пропитан духом старого доброго ООП. При каких-либо событиях меняется состояние объекта, а отображение объекта обязано перерисоваться.

Вот при двойном клике на label отдельного пункта в todo-списке

У нас вызывается handleEdit(), который обращаеся к методу edit() для TodoApp, а в нем прописывается изменение состояния editing.

Далее в handleEdit() идет изменение состояния editText для TodoItem

Итак пару переменых определяющих стостояние приложения изменены. Теперь посмотрим на метод render() в TodoItem. И там у нас реакция на одну переменную (добавление класса для тега LI конкретного пункта списка)

И реакция на другую переменную (значение value для поля редактирования названия пункта todo-списка)

Изменилось состояние и код знает как для этого нового состояния перерисовать отображение.

Но вместе с тем код на React полон различных выражений, назначение и работа которых не очевидна. В иных местах требуется как следует сосредоточиться, чтобы понять код. Вспомним хотя бы иммитацию работы событий.

Пока что результат, который получен при создании приложения todomvc на ReactJs смотрится довольно неплохо. Но чтобы понять картину в целом придется разобрать код приложения в реализации с другими JS библиотеками и фреймворками.

Оставить ответ