JSON:API, React и Redux

Кухта Александр, ведущий веб-технолог
Что такое SuperJob?

  • 1 млн посетителей в день
  • 30 млн резюме
  • 2 млн приглашений на собеседования в месяц
Что такое SuperJob Frontend?


Что такое SuperJob Frontend?

  • 300 тыс строк кода
  • 1200 компонентов
  • 1000 из них подключены к стору
  • 2700 компонентов на странице поиска вакансий
Никогда не было, и вот опять
...ваших программистов надо уволить, переход на поиск с главной занимает 4 секунды!
Вбиваю запрос в поиск, а подсказки тормозят!
ЛАГАЕТ
Демо
https://kgtjo.codesandbox.io//
В чем проблема?

Store comment comment comment author author author likesCounter likesCounter likesCounter Selector mapStateToProps Component ⚛️ Comment
💎
У нас сущность - это объект:

  • id - ID сущности
  • type - тип сущности
  • attributes - произвольные поля
  • relationships - связанные сущности


Entity comment author likesCounter Store comment comment comment comment author author author author likesCounter likesCounter likesCounter likesCounter
В сторе,
нормализованное представление
{
  "comment": {
    "1": {
      "id": "1",
      "type": "comment",
      "attributes": {
        "body": "diffraction never killed nobody"
      },
      "relationships": {
        "author": {
          "data": { "type": "author", "id": "1" }
        }
      }
    }
  }
}
Где-то в mapStateToProps компонента,
После денормализации
const comment = getEntityById(state, { id, type: 'comment' });
 
console.log(
  comment.author.name,
  comment.body,
  comment.likes.count,
);
// "Fraunhoffer"  "diffraction never killed nobody"  7
Редьюсер
export function entities(state = {}, action) {
  const { id, type, ...entity } = action.payload;
 
  switch (action.type) {
    case 'ADD_ENTITY': return {
      ...state,
      [type]: {
        ...state[type],
        [id]: { ...entity, id, type },
      },
    }
  }
}
Селектор
const getEntityById = createSelector(
  ({ entities }) => entities,
  (_, { type }) => type,
  (_, { id }) => id,
  (entities, type, id) => denormalizeEntity(entities, type, id),
);
Задача

Объяснить селектору, что сущность,
а так же ее зависимости, не изменились


comments authors likesCounters comment comment comment comment author author author author likesCounter likesCounter likesCounter likesCounter Entity comment author likesCounter


prev comment author likesCounter === === === next comment author likesCounter
Демо
https://kgtjo.codesandbox.io/


MAIN THREAD


~10%
времени уходит только на сборку мусора
во время обновления сущности в сторе

А потом еще разок 😞


Сейчас

Определяем неизменность сущности и ее зависимостей во время чтения
Ключи кэширования
Добавляем/изменяем сущность в сторе —
генерируем UUID и пишем в поле $$cacheKey
{
  "id": "1",
  "type": "likesCounter",
  "$$cacheKey": genUuid()
}


Но есть одно но — инвалидация


resume portfolio experience education photo video university company ⚛️ Resume


resume portfolio experience education photo video university company ⚛️ Resume ???


Нужно обновить $$cacheKey
у всех зависимых сущностей
$$dependants
Храним указатели на зависимые сущности
{
  "id": "1",
  "type": "likesCounter",
  "$$cacheKey": "6fbeb9e1-5ef4-4a0c-8acc-fa8f4a843adf",
  "$$dependants": ["comment@1"]
}


resume portfolio experience education photo video university company ⚛️ Resume
Демо
https://kgtjo.codesandbox.io/


Вот теперь все круто и быстро 🎉
Обратная сторона простоты redux
const mapStateToProps = (state, { authorId }) => {
  const comments = getAllEntitiesOfType(state, { type: 'comment' });
 
  const authorComments = comments.filter(({ author }) => {
    return author.id === authorId;
  });
 
  return { authorComments };
};
MobX
MobX

  • Следит за тем, какие данные использует компонент
  • Обновляет компоненты только если эти данные поменялись
  • mobx@3.x.x не работает в IE11 😔
Спасибо за внимание



Контакты:

github: thers
telegram: thers0
email: me@thers.io