Хочу поделиться небольшим советом, как я обычно пишу forceUpdate на хуках.
Наверное, многие из вас видели уже что-то подобное в моих уроках. Но я думаю, у многих из вас есть вопрос, Айюб, а когда же вообще пригодится
Вот несколько use case’ов, в которых я его юзаю:
1) Подписка на сторонние сервисы в effect’ах, при изменении которых надо обновить компонент. Что-то похожее делал react-redux в своем прошлом мажоре (до появления useSyncExternalStore).
2) Тестинг того, насколько хорошо мемоизируется компонент. Просто создаешь forceUpdate, пихаешь его в window и смотришь в devtools, какие компоненты ререндарятся без нужды.
3) Бывает полезно при использовании некоторых библиотек, например, для виртуализации. Обычно, они отдают этот метод наружу сами, через ref.
Use case такой, что у компонента есть мемоизация, пропсы не поменялись, но его все равно надо перерендерить, так как мемоизация не учитывает все факторы.
Как пример, часто в компоненты виртуализации передается высота строки и кол-во строк, а если текст в одной из строк поменялся, это не поведет за собой апдейта.
Есть ли еще какие-то ситуации, когда вам приходится использовать forceUpdate?
#react #devtips
const [,forceUpdate] = useReducer(v => v + 1, 0);
Наверное, многие из вас видели уже что-то подобное в моих уроках. Но я думаю, у многих из вас есть вопрос, Айюб, а когда же вообще пригодится
forceUpdate
?Вот несколько use case’ов, в которых я его юзаю:
1) Подписка на сторонние сервисы в effect’ах, при изменении которых надо обновить компонент. Что-то похожее делал react-redux в своем прошлом мажоре (до появления useSyncExternalStore).
2) Тестинг того, насколько хорошо мемоизируется компонент. Просто создаешь forceUpdate, пихаешь его в window и смотришь в devtools, какие компоненты ререндарятся без нужды.
3) Бывает полезно при использовании некоторых библиотек, например, для виртуализации. Обычно, они отдают этот метод наружу сами, через ref.
Use case такой, что у компонента есть мемоизация, пропсы не поменялись, но его все равно надо перерендерить, так как мемоизация не учитывает все факторы.
Как пример, часто в компоненты виртуализации передается высота строки и кол-во строк, а если текст в одной из строк поменялся, это не поведет за собой апдейта.
Есть ли еще какие-то ситуации, когда вам приходится использовать forceUpdate?
#react #devtips
GitHub
react-redux/src/hooks/useSelector.js at 7.x · reduxjs/react-redux
Official React bindings for Redux. Contribute to reduxjs/react-redux development by creating an account on GitHub.
❤12👍5🔥2🍓1
Пока компилируется проект, реши написать еще один пост по наболевшей теме на ревью и в целом, во многих проектах.
Это тема касающаяся функционального программирования. Наверное многие из вас знают хайп вокруг функционального программирования, чистых функций и immutable data structures.
Но тут я вижу одну проблему, многие только знают какие методы являются immutable, но не знают какова их цена и пихают их везде, где только можно.
Приведу пример кода:
Кажется, все написано, как нужно - нету мутаций, все функции чистые, все по канонам ФП.
Но давайте посмотрим на этот код с другой стороны…
В сумме, у нас выходит 4 итерации и 4 массива, 3 из которых будут собраны сборщиком мусора!
Можно написать тот же самый код в “императивном” стиле, и получить 1-у итерацию и 0 лишних массивов.
Можно конечно долго спорить о том, что первый способ лучше читается и тд. Но если адекватно назвать все переменные и не делать вложенные if’ы, то такой код будет не менее читаем, чем функциональный.
Да и работать будет в разы быстрее. Есть примеры еще более странные, но часто такое можно увидеть от новичков:
Если вдруг кто не знает concat - immutable метод, который объединяет 2 массива в один. В таком случае у нас вообще будет кубическое время и память.
И к тому же, в данном случае
В итоге, мутация у нас есть только внутри функции, где код полностью под нашим контролем. В таких НОРМАЛЬНО и даже НУЖНО использовать мутирующие методы.
Основаня идея immutable кода, это то, что не нужно лишний раз мутировать те переменные, которые пришли вам в конфиги/параметры/конструктор, любые внешние переменные, которые не контролируются вашим куском кода.
Вот такие дела.
#devtips #fp #react
Это тема касающаяся функционального программирования. Наверное многие из вас знают хайп вокруг функционального программирования, чистых функций и immutable data structures.
Но тут я вижу одну проблему, многие только знают какие методы являются immutable, но не знают какова их цена и пихают их везде, где только можно.
Приведу пример кода:
const stickyRows = Object.entries(this.props.stickyRows || {})
.map(([start, end]) => [parseInt(start, 10), end])
.filter(([start, end]) => {
return start < lastRow && (!end || end >= lastRow);
})
.map(([index]) => index);
Кажется, все написано, как нужно - нету мутаций, все функции чистые, все по канонам ФП.
Но давайте посмотрим на этот код с другой стороны…
Object.entries - O(n) память, O(n) cpu.
map - O(n) память, O(n) cpu, а тут их еще 2.
filter - тоже самое.
В сумме, у нас выходит 4 итерации и 4 массива, 3 из которых будут собраны сборщиком мусора!
Можно написать тот же самый код в “императивном” стиле, и получить 1-у итерацию и 0 лишних массивов.
const stickyRowsArray: number[] = [];
for (const key in stickyRowsMap) {
if (!hasOwn(stickyRowsMap, key)) {
continue;
}
const start = parseInt(key);
const end = stickyRowsMap[key];
const isInRange = start < lastRow && (!end || end >= lastRow);
if (!isInRange) {
continue;
}
stickyRowsArray.push(start);
}
Можно конечно долго спорить о том, что первый способ лучше читается и тд. Но если адекватно назвать все переменные и не делать вложенные if’ы, то такой код будет не менее читаем, чем функциональный.
Да и работать будет в разы быстрее. Есть примеры еще более странные, но часто такое можно увидеть от новичков:
function flattenOne(arrays) {
return arrays.reduce((result, array) => result.concat(array), []);
}
Если вдруг кто не знает concat - immutable метод, который объединяет 2 массива в один. В таком случае у нас вообще будет кубическое время и память.
O((N^2+2MN)/2)
, если быть точным, где M - размер вложенных массивов, а N - размер arrays.И к тому же, в данном случае
result.push
никак не испортил бы иммутабельность функции flattenOne
. reduce
создает полностью новый массив, переданный в параметр массив тоже arrays
никак не мутируется. В итоге, мутация у нас есть только внутри функции, где код полностью под нашим контролем. В таких НОРМАЛЬНО и даже НУЖНО использовать мутирующие методы.
Основаня идея immutable кода, это то, что не нужно лишний раз мутировать те переменные, которые пришли вам в конфиги/параметры/конструктор, любые внешние переменные, которые не контролируются вашим куском кода.
Вот такие дела.
#devtips #fp #react
❤19👍10🔥1💯1🍓1
Всем привет!
Пока готовил видео начал в очередной раз задумываться о том, что некоторые хуки являются просто обертками, для того, чтобы упростить работу разработчика. Однако их можно написать самим.
Самый очевидный пример - это
Однако если задуматься по глубже, то хуки, которые реально нужны из коробки - это useState, useEffect, useLayoutEffect, useContext, useDebugValue, ну и некоторые новые хуки, добавленные в React 18.
Давайте в качестве упражнения напишем хуки
И так,
Тут мы как раз пользуемся тем, что
Хорошо, давайте теперь напишем
Как мы видим, тут тоже логика получается не такой сложной. Мы просто используем переданный reducer для того, что создавать новый стейт.
В целом, все хуки, которые мы описали, не так сложно написать самому. И я думаю многие из вас могли уже догадываться о том, что они просто добавлены для чтого, чтобы сделать жизнь разработчика проще.
Однако какие еще хуки мы могли бы написать сами?
А что по поводу
Он ведь просто создает мутабельный объект, привязанный к компоненту. Наверное простым решением было бы использование
Но для реализации
Каким еще образом мы можем создать наш объект так, чтобы он был привязан к компоненту и не изменялся от рендера к рендеру?
И тут мне пришла странная идея:
По идее нам ничто не мешает создать наш объект один раз через
Но для реализации
Не знаю, чем вам эта информация может быть полезна, кроме как для более детального понимания устройства React. Однако надеюсь вы получили тот же фан, что и я при реализации данных хуков.
#devtips #react
Пока готовил видео начал в очередной раз задумываться о том, что некоторые хуки являются просто обертками, для того, чтобы упростить работу разработчика. Однако их можно написать самим.
Самый очевидный пример - это
useCallback
:
function useCallback(cb, deps) {
return useMemo(() => cb, deps);
}
Однако если задуматься по глубже, то хуки, которые реально нужны из коробки - это useState, useEffect, useLayoutEffect, useContext, useDebugValue, ну и некоторые новые хуки, добавленные в React 18.
Давайте в качестве упражнения напишем хуки
useReducer
, useMemo
, useCallback
сам.И так,
useCallback
мы уже покрыли, давайте следующим шагом покроем useMemo
:
function areDepsEqual(prevDeps, deps) {
if (prevDeps.length !== deps.length) {
return false;
}
return prevDeps.every((item, index) => item === deps[index]);
}
function useMemo(factory, deps) {
const prevDepsRef = useRef(null);
const result = useRef(null);
const prevDeps = prevDepsRef.current;
useEffect(() => {
prevDepsRef.current = deps;
});
if (!prevDeps || !areDepsEqual(prevDeps, deps)) {
result.current = factory();
}
return result.current;
}
Тут мы как раз пользуемся тем, что
useRef
возвращает ссылку на один и тот же объект, поэтому мы можем просто сравнить массив зависимостей и в случае чего обновить значение в объекте.Хорошо, давайте теперь напишем
useReducer
:
function useReducer(reducer, initialState) {
const [state, setState] = useState(initialState);
const reducerRef = useRef(reducer);
useLayoutEffect(() => {
reducerRef.current = reducer;
});
const dispatch = useCallback((action) => {
setState((state) => reducerRef.current(state, action));
}, []);
return [state, dispatch];
}
Как мы видим, тут тоже логика получается не такой сложной. Мы просто используем переданный reducer для того, что создавать новый стейт.
В целом, все хуки, которые мы описали, не так сложно написать самому. И я думаю многие из вас могли уже догадываться о том, что они просто добавлены для чтого, чтобы сделать жизнь разработчика проще.
Однако какие еще хуки мы могли бы написать сами?
useEffect`/`useLayoutEffect`/`useInsertionEffect
- все требуют вызова в определенный момент жизни компонента, поэтому их тут самому не написать.useDebugValue`/`useDeferredValue`/`useTransition
- тоже все завязаны на внутренности React.useState
должен как-то прикрепляться к компоненту и хранить значение между рендерами, так что тут тоже ничего не сделаешь.А что по поводу
useRef
?Он ведь просто создает мутабельный объект, привязанный к компоненту. Наверное простым решением было бы использование
useMemo
:
function useRef(value) {
const [ref] = useMemo(() => ({ current: value }), []);
return ref;
}
Но для реализации
useMemo
мы уже используем useRef
...Каким еще образом мы можем создать наш объект так, чтобы он был привязан к компоненту и не изменялся от рендера к рендеру?
И тут мне пришла странная идея:
function useRef(value) {
const [ref] = useState({ current: value });
return ref;
}
По идее нам ничто не мешает создать наш объект один раз через
useState
и дальше уже его мутировать. Да и по канонам React стейт мутировать нельзя. Но это связанно с тем, что мутации не поведут за собой обновление компонента, однако другие части приложения будут использовать последнее значение стейта.Но для реализации
useRef
тут никаких проблем не должно быть. Наш “рефовый” стейт и так никогда не будет обновляться.Не знаю, чем вам эта информация может быть полезна, кроме как для более детального понимания устройства React. Однако надеюсь вы получили тот же фан, что и я при реализации данных хуков.
#devtips #react
👍43❤20🔥7💯3🍓1
Сидел тут лазил в исходинках React (хотел уточнить один момент касательно батчинга), и наткнулся на такую вещь, как
Решил загуглить, оказалась, что это АПИ которое позволяет узнать, есть ли какие-то ивенты от пользователя, ожидающие обработку.
Нужно оно для того, чтобы во время больших синхронных тасок прерывать выполнение JS и дать браузеру возможность произвести нужные действия.
Подробнее про саму эту фичу можно почитать вот тут — https://developer.chrome.com/articles/isinputpending/
Из минусов, поддержка на данный момент только в Chrome. Учитывая то, что вышла статья в 2020 - кажется, что особого интереса в реализации у других браузеров нету.
UPD: кажется, что есть пропоусал на добавление в стандарты — https://github.com/WICG/is-input-pending
#devtips #js #react
navigator.scheduling.isInputPending
.Решил загуглить, оказалась, что это АПИ которое позволяет узнать, есть ли какие-то ивенты от пользователя, ожидающие обработку.
Нужно оно для того, чтобы во время больших синхронных тасок прерывать выполнение JS и дать браузеру возможность произвести нужные действия.
Подробнее про саму эту фичу можно почитать вот тут — https://developer.chrome.com/articles/isinputpending/
Из минусов, поддержка на данный момент только в Chrome. Учитывая то, что вышла статья в 2020 - кажется, что особого интереса в реализации у других браузеров нету.
UPD: кажется, что есть пропоусал на добавление в стандарты — https://github.com/WICG/is-input-pending
#devtips #js #react
👍16❤2🔥2💯2⚡1🍓1
Всем привет!
Сегодня хотел бы поговорить про полную мемоизацию всех колбэков в компонентах и поделиться своим мнением по данному вопросу.
Для тех кто не слышал, часто в проектах бывает такой конвеншен, когда все пропсы нужно обязательно мемоизировать, прежде чем передавать в компонент ниже.
Я когда в первый раз увидел такие договоренности на проекте, то был очень удивлен. Ведь в большинстве случаев рендер компонента и так быстрый, а мемоизация добавляет сложности с актуализацией депсов в
Однако React сейчас работает над компилятором, цель которого как раз заключается в том, чтобы самому мемоизировать все компоненты/пропсы. Также посмотрев на некоторые проекты я понял, что у данного подхода есть и плюсы.
Самый очевидный наверное в том, что есть какой-то стандарт в проекте. Нету лишних комментов на ревью о ненужном
Еще одним из плюсов является то, что некоторые пропсы могут быть переданы вниз на много уровней, и не всегда ты можешь узнать используется ли
Поэтому мое мнение по данному вопросу перестало быть настолько однозначным. На самом деле, я думаю что на большом проекте, где есть много разработчиков разного уровня и у многих из них нету глубокого понимания React, данный подход будет полезен.
А в других случаях я бы предпочел все равно использовать мемоизацию только там, где это нужно. Можно придерживаться таких правил:
- Мемоизируем все пропсы для компонентов из внешних библиотек (если точно не знаем, что там внутри).
- Мемоизируем пропсы для компонентов, которые используют memo.
- Если есть пропсы откуда-то с верхнего уровня и они не мемоизированы - можно использовать
Также хотел бы отметить, что если уж и мемоизировать все подряд, то намного проще использовать самописные хуки
#devtips #react #hooks
Сегодня хотел бы поговорить про полную мемоизацию всех колбэков в компонентах и поделиться своим мнением по данному вопросу.
Для тех кто не слышал, часто в проектах бывает такой конвеншен, когда все пропсы нужно обязательно мемоизировать, прежде чем передавать в компонент ниже.
Я когда в первый раз увидел такие договоренности на проекте, то был очень удивлен. Ведь в большинстве случаев рендер компонента и так быстрый, а мемоизация добавляет сложности с актуализацией депсов в
useMemo/useCallback
.Однако React сейчас работает над компилятором, цель которого как раз заключается в том, чтобы самому мемоизировать все компоненты/пропсы. Также посмотрев на некоторые проекты я понял, что у данного подхода есть и плюсы.
Самый очевидный наверное в том, что есть какой-то стандарт в проекте. Нету лишних комментов на ревью о ненужном
useCallback
. Также не идут споры о том, стоит ли что-то мемоизировать или нет.Еще одним из плюсов является то, что некоторые пропсы могут быть переданы вниз на много уровней, и не всегда ты можешь узнать используется ли
memo
где-то внизу дерева, либо используются ли эти пропсы в качестве депсов для колбэков или эфектов ниже.Поэтому мое мнение по данному вопросу перестало быть настолько однозначным. На самом деле, я думаю что на большом проекте, где есть много разработчиков разного уровня и у многих из них нету глубокого понимания React, данный подход будет полезен.
А в других случаях я бы предпочел все равно использовать мемоизацию только там, где это нужно. Можно придерживаться таких правил:
- Мемоизируем все пропсы для компонентов из внешних библиотек (если точно не знаем, что там внутри).
- Мемоизируем пропсы для компонентов, которые используют memo.
- Если есть пропсы откуда-то с верхнего уровня и они не мемоизированы - можно использовать
useEvent/useLatest
для того, чтобы избежать лишних обновлений.Также хотел бы отметить, что если уж и мемоизировать все подряд, то намного проще использовать самописные хуки
useEvent
и useLatest
, так как большинство колбэков вообще никогда не должны обновляться.#devtips #react #hooks
❤24👍8🔥6💯2🍓1
Всем привет!
Наверняка многие из вас уже успели посмотреть мое последнее видео про всплывающие окна.
Там я использовал вложенные контексты с одним типом, для того, чтобы отслеживать иерархию наших компонентов.
Так вот, впервые я увидел данную технику в коде react-redux, и тогда я не совсем понял, для чего это вообще нужно.
Затем через какое-то время наткнулся на issue про stale props и zombie children и на пост приложенный ниже о том, как данная проблема была решена для connect.
В целом, статья очень полезная для понимания redux и многих его концептов.
Да, он уже не настолько актуален после появления react-query и других cache management решений, однако все равно считаю ее очень полезной. Как минимум для общего кругозора.
А если повезет, может как и я извлечете пару полезных идей для своих проектов!
https://kaihao.dev/posts/Stale-props-and-zombie-children-in-Redux
#devtips #react #redux
Наверняка многие из вас уже успели посмотреть мое последнее видео про всплывающие окна.
Там я использовал вложенные контексты с одним типом, для того, чтобы отслеживать иерархию наших компонентов.
Так вот, впервые я увидел данную технику в коде react-redux, и тогда я не совсем понял, для чего это вообще нужно.
Затем через какое-то время наткнулся на issue про stale props и zombie children и на пост приложенный ниже о том, как данная проблема была решена для connect.
В целом, статья очень полезная для понимания redux и многих его концептов.
Да, он уже не настолько актуален после появления react-query и других cache management решений, однако все равно считаю ее очень полезной. Как минимум для общего кругозора.
А если повезет, может как и я извлечете пару полезных идей для своих проектов!
https://kaihao.dev/posts/Stale-props-and-zombie-children-in-Redux
#devtips #react #redux
Kai Hao
Stale props and zombie children in Redux | Kai Hao
If you have read the react-redux v7 release documentation, you might have come across the section where it mentioned the stale props and "zombie children" problem. Even though it's…
❤18👍8⚡2💯2👌1🍓1
Всем привет!
Сегодня один из подписчиков скинул мне ссылку на доку из библиотеки с универсальными хуками. Там сказано, что хук
При первом прочтении я удивился, так как об этих изменениях я не слышал, и в доке про 18-ую версию тоже ни слова об этом (даже в секции changelog, где есть ссылки на коммиты с изменениями).
Однако в доке библиотеки была ссылка на discussions, где Ден Абрамов пишет про удаление ворнинга (про причины можете сами почитать по ссылке ниже). Также сам проверил в коде, логов больше никаких нету.
В целом, решение считаю верным, но очень не понравилось то, что об этом явно нигде не говорилось, хотя это, как мне кажется, важно знать.
https://github.com/reactwg/react-18/discussions/82
#devtips #react
Сегодня один из подписчиков скинул мне ссылку на доку из библиотеки с универсальными хуками. Там сказано, что хук
useSafeState
станет deprecated, так как warning о обновлении стейта в демонтированном компоненте был удален в react 18.При первом прочтении я удивился, так как об этих изменениях я не слышал, и в доке про 18-ую версию тоже ни слова об этом (даже в секции changelog, где есть ссылки на коммиты с изменениями).
Однако в доке библиотеки была ссылка на discussions, где Ден Абрамов пишет про удаление ворнинга (про причины можете сами почитать по ссылке ниже). Также сам проверил в коде, логов больше никаких нету.
В целом, решение считаю верным, но очень не понравилось то, что об этом явно нигде не говорилось, хотя это, как мне кажется, важно знать.
https://github.com/reactwg/react-18/discussions/82
#devtips #react
❤11👍6💯2🏆1🍓1
Сегодня помогал человеку с реализацией draggable canvas (по типу миро, но на html).
Столкнулся с проблемой, когда код внутри setState колбэка вызывался 2 раза.
Код выглядел примерно так:
Благо из-за серых логов быстро смог понять, в чем причина. Но все равно выглядит очень странно.
Да, по задумке колбэк должен быть pure function. Но почему нету никакого лога о том, что второй вызов выдал другой результат?
В общем, при обновлении стейта будьте внимательнее.
#devtips #react
Столкнулся с проблемой, когда код внутри setState колбэка вызывался 2 раза.
Код выглядел примерно так:
setPosition(position => {
const deltaX = e.clientX - prevMousePosition.x;
const deltaY = e.clientY - prevMousePosition.y;
prevMousePosition = {
x: e.clientX,
y: e.clientY,
}
return {
x: position.x + deltaX / scale,
y: position.y + deltaY / scale,
}
В итоге при первом вызове стейт вставал в правильное значение, а при втором — все сбрасывалось, так как prevMousePosition
был уже таким же, что и clientX/clientY из ивента.Благо из-за серых логов быстро смог понять, в чем причина. Но все равно выглядит очень странно.
Да, по задумке колбэк должен быть pure function. Но почему нету никакого лога о том, что второй вызов выдал другой результат?
В общем, при обновлении стейта будьте внимательнее.
#devtips #react
GitHub
React.StrictMode causes setState to fire twice · Issue #12856 · facebook/react
Do you want to request a feature or report a bug? Bug What is the current behavior? When wrapping a with React.StrictMode, setState is fired twice If the current behavior is a bug, please provide t...
👍18🏆2❤1👎1🔥1🍓1