Comments 14
Чтобы правильно (и оптимально, что важно) все посчитать redux должен по некому события сделать «что должен», после чего как-то тригернуть следуйщий этап. В редьсерах такого не сделать.
Быть может сага? В принципе она может ожидать прихода событий и вызывать функции обработчики, а они уже будут вызывать известные им этапы и будет все работать просто и удобно. (надо будет написать хелпер для такого, спасибо за идею)
А что делать если послали два события? Тогда все посчитается два раза, чтобы этого избежать, то прийдется «для расчета следуйщего этапа» тоже кидать событые, и надется что takeLatest сделает все правильно.
В общем я постарался решить задачу без redux, потому что не redux-ом единым. В нем хорошо хранить данные, но переключение направления сортировки или страницы — личное дело компонента отображения.
Сами люди из facebook пишут, что getDerivedStateFromProps в крайне редких случаях должен использоваться.
Он вроде как должен помогать решать сложные задачи работы со стейтом, но тут нет стейта — только результат который надо показать на основе текущего стейта.
Всегда и везде это было в mapStateToProps, на стороне view(в react-redux), но никак не в redux-core.
Reselect composition в mapStateToProps сделает тоже самое, что и Reselect composition описанная в этой статье, только в возможно более «правильном» месте и будет болеть теми же самыми проблемами. Заменить reselect на memoize-state(он для того и был придуман) — и дело в шляпе.
Если делать пейджер то все что он должен «знать» это сырые данные (props.allRows), и номер отображаемой страницы (state). Вроде все. Какие еще state.visible могут быть у пейджера? это же задача вьюхи самой страницы.
Как ни пытался — не получилось уловить всю нить повествования. Что, где, как, почему? ;) Я так и не понял в чём там вообще была проблема. Но как я понял, поплевавшись на вложенные 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-ы крутая штука, но явно не в этой задаче.
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% не справится, в то время как такая вот развесистая конструкция у нее проблем не вызывает.
— все думаю перенести все дела из getDeviredStateFromState в componentDidUpdate, потому что второй представляет и данных побольше, и сайдэффекты «разрешает».
— зачем писать свой getDeviredStateFromProps если он вам не нужен, и вообще у вас Stateless компонент?
Но на самом деле — почему бы и не использовать прямо в render. Проблем нет.
getDerivedStateFromState – или как сделать из простой проблемы сложную