Naararouter
@Naararouter
Junior Fullstack Web Developer

Как заставить mobx НЕ ре-рендерить всех детей массива?

Допустим, у нас есть массив из элементов (дочерние компоненты), необходимо его отрендерить при загрузке. А затем добавлять/удалять его элементы. Задача в том, чтобы сделать это максимально производительно, с точки зрения скорости рендеринга компонентов.
Собственно, начал серьёзно задумываться над этими вопросами, как раз после использования тулзов разработчика mobx, которые наглядно показывают время рендера компонентов.

При изменении массива, MobX перерисовывает абсолютно все дочерние элементы, встречая обыкновенную конструкцию вида elements.map(data => <El data={data} />);при том, что его elements в store "задекорированы" в @observable. Атрибуты key он не воспринимает, т.к. игнорирует shouldComponentUpdate (исходя из документации, пытался найти какие-то обходные решения, костыли, но даже с последними крайне не густо).

Провёл перформанс-тесты для своих исходных данных:
Дочерний элемент представляет из себя material-ui checkbox, вычисляемую текстовую метку и две иконки-контрола. Ничего особенного в общем.

MobX - на 150 дочерних элементов, на создание следующего (151) уходит примерно ~145-155ms. Примерно такой же результат выдал и "голый" React, генерируя разные keys, каждый раз, что однозначно заререндерить весь список снова.
Но вот прописав банальный shouldComponentUpdate (return false для всех былых дочерных элементов) к обычному React'y - обработка этих действ заняла всего 10-15ms.
Хотелось бы как-то решить эту проблему... либо, возможно найти достойное оправдание такому поведению.

[Update#1]: Для бОльшей наглядности я опубликовал проект, иллюстрирующий проблему (без экспериментов с computed):
Проект: https://github.com/Naararouter/mobx-problem-001
Девелопер-тулз: https://chrome.google.com/webstore/detail/mobx-dev...
Буду бесконечно благодарен, если удастся посмотреть и найти решение, либо направить меня по "пути истинному".

P.s.: проект - модель реальной задачи, в итоге получилось что-то похожее на банальный ToDo List. Постарался максимально абстрагироваться от контекста, сохранив структуру компонентов в реальных условиях.
  • Вопрос задан
  • 707 просмотров
Пригласить эксперта
Ответы на вопрос 2
Laiff
@Laiff
Front-end developer
Возможно имеет смысл посмотреть на https://mobx.js.org/refguide/computed-decorator.html плюс если там нет ключей, то реакт начинает грустить поэтому ключи задавать нужно хотя бы индексом
Ответ написан
@bgnx
Проблема в вашем коде, в каждом дочернем компоненте вы обращаетесь к целому массиву, mobx видит что массив изменился, значит нужно вызвать перерендер всех компонентов которые обращаются к массиву. В вашем же случае обращение происходит потому что в компоненте ProblemChildren вы в рендер-методе вызваете функцию getCurrentFromStore() которая вызывает функцию .getCurrentFromKey(this.props.id) которая уже и обращается к самому массиву this.problemEntitiesList.find(x => x.id === key) а значит при любом изменении массива (добавление, удаление, перезапись элемента) будет перерендер этого компонента. Если не обращаться к массиву то ре-ререндера не будет как вот в этом примере https://codesandbox.io/s/wynyl96j07 Вам нужно переписать чтобы нужные данные передавались через пропсы потому что в вашем коде какой-то треш - зачем рендерить <ProblemChildren key={data.id} id={data.id} /> передавая айдишник чтобы потом в самом компоненте вытащить этот же объект data через стор this.props.ProblemStore.getCurrentFromKey(this.props.id) ? Вам не кажется что проще было бы сразу передать объект data через пропс и не делать двойную работу? При этом и лишних ре-рендеров не происходило бы.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы