Использование Redux для управления всем состоянием вашего приложения - это одно. Но убедиться, что структура вашего состояния оптимальна, чтобы ваш код был поддерживаемым и эффективным, - это совершенно новая игра с мячом!
Управление формой состояния вашего приложения абсолютно необходимо. По мере роста вашего приложения, если ваша форма состояния плохо организована, управление ею может быстро стать беспорядочным и даже замедлить реакцию вашего приложения.
Если вы используете React как интерфейсную структуру, скорее всего, вы можете использовать Redux для управления состоянием своего приложения. В этом случае вы реализуете один или несколько редукторов для изменения состояния вашего приложения каждый раз, когда действие отправляется в ответ на действия пользователей. Давайте посмотрим ниже, как перейти от первоначальной неэффективной структуры формы состояния к наиболее эффективной.
Подход формы начального состояния
Предположим, мы пишем приложение, в котором пользователи могут публиковать твиты, и каждый твит имеет комментарии. Предположим также, что твиты могут быть организованы по темам или темам. Базовая и неэффективная форма состояния будет следующей:
Как видите выше, у нас сложная вложенная структура. Такая форма состояния оказывается неэффективной по нескольким причинам:
- Он глубоко вложен, поэтому изменение состояния для обновления свойства твита требует, чтобы разработчик написал большой объем кода в редукторе.
- Массив
tweets
выполняет две функции: он хранит данные (то есть отдельный объектtweet
) и упорядочивает твиты, поскольку твиты находятся внутри массива. - В этой ситуации массивы неэффективны. Представьте, что у вас в ветке 50 000 твитов. На поиск подходящего твита внутри массива потребуется время. Время поиска внутри массива равно O (n), так как компьютеру необходимо просмотреть весь список элементов, пока не найдет нужный.
- Обратите внимание, что для примера я даже не включил комментарии, относящиеся к каждому
tweet
в приведенной выше структуре, так как это было бы еще хуже.
Не используйте массивы для хранения сложных объектов. Как мы увидим позже, единственный смысл использования массивов в нашем состоянии - это упорядочивание элементов. Это потому, что, в отличие от массивов, мы не можем полагаться на объекты для упорядочивания содержащихся в них элементов.
Заменить массивы объектами
Простым и первым улучшением было бы использование объектов вместо массивов. Используя наш предыдущий пример и сосредоточив внимание только на массиве tweets
, мы могли бы легко изменить его следующим образом:
Мы изменили предыдущий массив tweets
и разделили выполняемую работу на
- Создание
byId
объекта, задача которого - хранить только отдельные твиты - Создание массива
allIds
, задача которого - сортировать твиты, но здесь мы можем просто указать идентификатор каждого твита вместо всегоtweet
.
Текущая структура уже намного эффективнее первой. Чтобы найти твит, мы будем искать его по объекту, а не по массиву. Время поиска по объекту - постоянное время, то есть O (1), что является большим улучшением по сравнению с O (n). Кроме того, нас больше не волнует положение каждого твита внутри объекта byId
. Массив allIds
предназначен для сортировки твитов, и теперь намного проще работать с таким базовым массивом, как allIds
.
Включая комментарии к каждому твиту
Не усложняя наш код, теперь мы можем добавлять комментарии, относящиеся к каждому твиту. Один из способов реализации будет следующим:
Такое формирование состояния оказывается намного более эффективным по сравнению с первым примером, который мы использовали. Внутри каждого объекта твита мы ссылаемся только на относящиеся к нему комментарии через массив идентификаторов комментариев. Где-то еще у нас есть объект comments
, внутри которого мы можем получить информацию о каждом комментарии.
Теперь у нас есть более «сплющенная» структура, над которой мы можем работать более эффективно. Обновление этой формы состояния потребует небольшого количества кода внутри наших редукторов. Наш код теперь легко поддерживать, и мы уменьшили вероятность ошибки.
Собираем все вместе
Посмотрим, каков будет конечный результат по сравнению с нашим первоначальным подходом.
В отличие от первого подхода, когда мы смешивали массивы и объекты, что затрудняло изменение и поддержку состояния, с этим окончательным подходом теперь легко работать. Наш код легко поддерживать, а наши поисковые операции выполняются очень быстро. Это явно победа!
Давайте рассмотрим несколько ситуаций, чтобы увидеть, что следует делать при изменении некоторых элементов:
Добавить твит
- Создайте новый объект
tweet
в объектеbyId
объектаtweets
- Добавьте идентификатор нового твита в массив
allIds
объектаtweets
- Добавьте идентификатор нового твита в массив
tweets
внутри соответствующего объектаthread
Редактировать твит
- Найдите выбранный объект
tweet
по его идентификатору внутри объектаbyId
объектаtweets
и обновите его
Удалить твит
- Найдите выбранный объект
tweet
по его идентификатору внутри объектаbyId
объектаtweets
и удалите его. - Удалите идентификатор твита внутри массива
allIds
объектаtweets
- Удалите идентификатор твита из массива
tweets
соответствующего объектаthread
.
Добавить комментарий
- Создайте новый объект
comment
в объектеbyId
объектаcomments
- Добавьте идентификатор нового комментария в массив
allIds
объектаcomments
- Добавьте идентификатор нового комментария в массив
comments
внутри соответствующего объектаtweet
Редактировать комментарий
- Найдите выбранный объект
comment
по его идентификатору внутри объектаbyId
объектаcomments
и обновите его.
Селекторы
Один из способов подумать о селекторах - провести параллель с геттером и сеттером. Dispatch похож на сеттер, поскольку он изменяет состояние, в то время как селектор похож на геттер, поскольку он извлекает данные.
Обычно вы будете использовать селекторы в компонентах контейнера при сопоставлении состояния с реквизитами с mapStateToProps
. Предположим (как показано в нашем примере), что каждый твит имеет свойство isEditable
, которое равно true
или false
, и что только один твит может быть редактируемым. Если вы хотите получить редактируемый твит, вы можете написать селектор, подобный приведенному ниже:
Я только что привел один пример, но вы можете написать столько селекторов, сколько вам нужно, в зависимости от данных, которые вы хотите получить.
Кроме того, как рекомендовано Дэном Абрамовым, лучше всего записывать свои селекторы в том же файле, что и редуктор. Это имеет смысл, особенно если вы думаете о них как о геттерах / сеттерах. Кроме того, вы также можете обратиться к документации Redux, которая предоставляет подробную информацию.
Удачи в реализации Redux!
Ваше здоровье!