Про сравнение объектов Immutable.js

Примерно год назад я начал переводить проект, над которым работаю в Charge.auto с жутко запущенного легаси на реакт-редакс-вот это всё. Для представления данных в сторе решил взять Immutable.js. Граблей было исхожено не одно поле, но на днях до меня наконец-то дошло, как разрешить одну из самых раздражающих меня проблем.

В приложении с сервера прилетает множество разных массивов данных, эти массивы раскладываются туда-сюда в сторе в List и дальше забираются в редакс-контейнерах и передаются как пропсы в компоненты. И вот тут начался праздник. Как правило, нужно выводить не весь список, а какую-то выборку по нему. Сначала я делал что-то типа

function mapStateToProps(store) {
  const items = store.get('beans').get('items');
  const activeItems = items.filter((item) => item.get('isActive'));

  return { activeItems };
}

Но почти сразу же я заметил, что компоненты постоянно обновляются, даже если данные не менялись. Причина проста — List.filter() создаёт новый инстанс отфильтрованного списка при каждом вызове, а в shouldComponentUpdate() использовалась просто строгое неравенство. На тот момент я ещё не до конца понимал всей кухни реакта и как писать реакт-компоненты правильно, поэтому просто стал передавать из контейнера в компонент полные списки, а фильтрацию и всё остально делать уже в компонентах. Но меня постоянно бесил тот факт, что приходится гонять туда-сюда жирные объекты, а компоненты получаются с излишней логикой.

Но вот на днях я нашёл целых два решения проблемы.

Во-первых, все объекты Immutable.js имеют метод .equals(), так что его и нужно использовать в shouldComponentUpdate()

shouldComponentUpdate(nextProps) {
  return !nextProps.activeItems.equals(this.props.activeItems);
}

Ну а во-вторых, можно пойти упарываться функциональщиной дальше и использовать мемоизацию (например, из библиотеки rambda)

function mapStateToProps(store) {
  const items = store.get('beans').get('items');
  const filterActive = R.memoize((list) => list.filter((item) => item.get('isActive')));
  const activeItems = filterActive(items);

  return { activeItems };
}

Такое вот простое решение. Теперь осталось отрефакторить всё.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *