Comments 14
«Более сложный пример» подозрительно напоминает reducer засунутый прямо во view и занимающий там явно много места. Можно было вынести весь этот list/sort и прочее в redux и сопокойно пользоваться getDerivedStateFromProps + actions.
Это, как не странно, может быть сложная задача. Проблема в именно в «Flow».
Чтобы правильно (и оптимально, что важно) все посчитать redux должен по некому события сделать «что должен», после чего как-то тригернуть следуйщий этап. В редьсерах такого не сделать.
Быть может сага? В принципе она может ожидать прихода событий и вызывать функции обработчики, а они уже будут вызывать известные им этапы и будет все работать просто и удобно. (надо будет написать хелпер для такого, спасибо за идею)
А что делать если послали два события? Тогда все посчитается два раза, чтобы этого избежать, то прийдется «для расчета следуйщего этапа» тоже кидать событые, и надется что takeLatest сделает все правильно.

В общем я постарался решить задачу без redux, потому что не redux-ом единым. В нем хорошо хранить данные, но переключение направления сортировки или страницы — личное дело компонента отображения.
+1. Палкой по рукам бить надо, когда логику во view layer пихают.
Сами люди из facebook пишут, что getDerivedStateFromProps в крайне редких случаях должен использоваться.
Мне казалось, что вопрос о том что React не является view layer давно закрыт. Как и вопрос об уместности постоянного использования redux.
Он вроде как должен помогать решать сложные задачи работы со стейтом, но тут нет стейта — только результат который надо показать на основе текущего стейта.
Всегда и везде это было в mapStateToProps, на стороне view(в react-redux), но никак не в redux-core.
Reselect composition в mapStateToProps сделает тоже самое, что и Reselect composition описанная в этой статье, только в возможно более «правильном» месте и будет болеть теми же самыми проблемами. Заменить reselect на memoize-state(он для того и был придуман) — и дело в шляпе.
Эх, я так и не понял зачем в данных ситуациях пытались юзать componentWillReceiveProps. ((
Если делать пейджер то все что он должен «знать» это сырые данные (props.allRows), и номер отображаемой страницы (state). Вроде все. Какие еще state.visible могут быть у пейджера? это же задача вьюхи самой страницы.
state.visible — это копипаст конкретной боли конкретного человека из конкретного issue. Разговор переходит на задачу с таблицей парой абзацев ниже.

Как ни пытался — не получилось уловить всю нить повествования. Что, где, как, почему? ;) Я так и не понял в чём там вообще была проблема. Но как я понял, поплевавшись на вложенные WeakMap-ы мы пришли к Proxy с трекингом зависимостей. Ух.


Итак. Нам нужно взять таблицу и отсортировать. У нас есть data и есть sort. Мемоизируем это тем же createSelector-ом (можно и не им, не принципиально):


const getSortedTable = createSelector(
  obj => obj.data,
  obj => obj.sort,
  (data, sort) => magic(data, sort));

const sortedData = getSortedTable({ data, sort });

Каждый холостой вызов getSortedTable будут вызваны две крохотные функции и будет произведено три ===. Затем что нам надо? Взять slice? Ну ок:


const getPageData = createSelector(
  obj => obj.data,
  obj => obj.page,
  (data, page) => anotherMagic(data, page));

const sortedData = getPageData({ data: sortedData, page });

Картинка та же: два () => select и три ===. Зачем тут weakMap-ы? Зачем тут proxy?


kashey где я потерял нить и свернул не туда?


P.S. вложенные weakmap-ы крутая штука, но явно не в этой задаче.

Нить нигде не потеряна, и поворот не пройден. Только кода получилось примерно в 6 раз больше чем в первом примере на мемоизацию.
  getSortedData = (lodash)memoize(magic)
  getPagedData = memoize(anotherMagic)

  render() {
    const sorted = this.getSortedData(this.props.data, this.props.sort);
    const pages = this.getPagedData(sorted, this.props.page);
  }

Вся проблема в том как это написать так чтобы было просто, удобно и всем понятно. В случае с reselect или обычной мемозаиций — каждый шаг понятен, но не понятно как они сочетаются.
Тут — сильно понятнее и короче. И декларативнее.
memoizedFlow([
    ({data, sort}) => ({ data: magic(data, sort)}),
    ({data, page}) => ({ data: anotherMagic(sorted, page)});
])

WeakMapов тут нет, это какой-то ботаник начал их городить непонятно зачем и почему. И proxy тут нет, так как работать должно под IE11/React Native. (на самом деле есть и то и другое, но не всегда)

А скорость? memoize-state просто знает какие ключи были использованы для формирования результата и проверяет их. При этом достаточно умно, понимая вложеность обьектов и как вообще «современная иммутабельность» работает. Я к тому, что при холостой работе там будет те же самые два ===, под одному на каждую функцию. В общем perf тесты часть репозитория, согластно измерениям там сотни тысяч, а то и миллионы мемоизированных операций в секунду. Чуть чуть медленее reselect или memoize-one.
но не понятно как они сочетаются

А почему непонятно? У тебя же тут по сути всего 2 строки (пример с lodash/memoize), как в них можно запутаться? :) Ну или спрошу по-другому: неужели весь этот код, что в разделе "Или можно?" с <MemoizedFlow/>, клеевым-редьюсером и function-as-children проще и понятнее, этих двух строк? :) Что мы выиграли непосредственно в этом примере с таблицей.

Это тут все понятно. А если вы приходите на готовый проект (или уходите с такого) — было бы хорошо иметь совсем-совсем понятную логику.
В принципе все так называемые «code smells» и антипатерны примерно об этом — оно вообще работает, но рано или поздно все сломается. Когда новый джун выйдет, например.
У меня за джуна выступает жена, для которой до сих пор составляет проблему написать js, c правильным reselect она 100% не справится, в то время как такая вот развесистая конструкция у нее проблем не вызывает.
А почему не использовать memoizedFlow из memoize-state прямо в getDeviredStateFromProps? Суть остается та же, зато render() не захламляется.
Основых идей две:
— все думаю перенести все дела из getDeviredStateFromState в componentDidUpdate, потому что второй представляет и данных побольше, и сайдэффекты «разрешает».
— зачем писать свой getDeviredStateFromProps если он вам не нужен, и вообще у вас Stateless компонент?

Но на самом деле — почему бы и не использовать прямо в render. Проблем нет.
Ну это как-то странно. Во-первых, переносить в componentDidUpdate и обновлять там state — значит рендерить два раза. Во-вторых, MemoizedFlow хоть и позволяет в некоторых случаях писать компонент без стейта, все же представляет собой стейт, хоть и неявный. Так что stateless'ом тут не очень пахнет.
Ну вот потому и не переношу :)
А про stateless — MemoizedFlow сам то stateful, но никак не регламентирует чем должны быть «вы».
Only those users with full accounts are able to leave comments. Log in, please.