Комментарии 8
А есть какой нибудь пример большого проекта, где моно было бы увидеть это все в реальном проекте, а не на уровне одной кнопки ?
Вопрос: А почему в разделе "Какие проблемы решают callback refs в коде" в первом примере вы говорите, что обычный useRef не работает? Там же:
function useResizeObserver(...) {
useEffect(() => {
...
return () => {
resizeObserver.unobserve(element);
};
}, [...]);
}
Т.е. должно отписаться и все ок. Так же у вас есть: observer.disconnect()
. Вы его и используете в примерах с коллбеками, так как там нет ссылки на элемент, когда его выключаете. Можно этот метод тут использовать.
Я к тому, что почему выше указанный пример не работает? Вроде бы все должно быть ок.
В целом техника нормальная. Не знал про нее. Благодарю за статью. Как минимум можно иногда избежать хранения ref переменной в компоненте.
Не будем тратить время на детальное объяснение того, что происходит в коде выше
Давайте все таки разберемся. Вы используете хук useRef, который создает объект с полем current на все время жизни компонента. И ссылка на объект останется неизменной. Это важно.
Далее в этот объект вы в зависимости от условия кидаете новые ссылки на DOM элементы.
Но в хук useResizeObserver
вы все также передаете ту статичную ссылку на объект рефа. Это первый звоночек, который я заметил. И далее в хуке заметил, что useEffect тоже подписан на ту самую ссылку, которая статична.
Попробуйте не переписывая ничего заменить:useResizeObserver({ elemRef, onResize: handleResize });
наuseResizeObserver({ elemRef: elemRef.current, onResize: handleResize });
На сколько я помню реакт не будет дергать эффекты при изменении elemRef.current
Ну как же. В самом описании хука useEffect написано, что если значение в массиве зависимостей отлично от предыдущего рендера, то реакт дернет useEffect
Да, там нюанс, когда эта проверка происходит. Изменение ref не провоцирует рендер, соответственно хук не вызовется. А вот когда будет следующий рендер (если будет) тогда будет чек зависимостей и вызов хука с новым значением.
export function App(){
const ref = useRef<any>(null);
useEffect(() => {
console.log('ref changed', ref.current);
ref.current = { };
}, [ref.current]);
return <div ref={ref}/>;
}
В консоль 1 запись с дивом.
Даже если мы в кнопке будет задавать ref.current, не будет ни рендера ни эффекта.
Но если мы в кнопке вызовем useState
и спровоцируем ререндер, эффект вызовется второй раз даже если не менять ref.current: при первом рендере там null, при следующих там {}
(ну или div если фигней не страдать)
ааа, вы про это.
конечно изменение рефа ничего не триггерит
Я кажется понял вас.
Нет, я не хочу изменяя ref.current заставлять компоненту ререндерится.
Мне не нравится решение проблемы, которое предложил автор. Хук переписан так, что с наскоку непонятно, что в нем происходит, приходится вчитываться, а я это не люблю.
Я бы предложил решать проблему все таки через ссылку и первую реализацию(якобы косячную), потому что она приятно и легко воспринимается.
Всего лишь вместо
const elemRef = useRef();
useResizeObserver({ elemRef, onResize: handleResize })
<div ref={ref}/>
Сделать
const [elemRef, setRef] = useState();
useResizeObserver({ elemRef, onResize: handleResize })
<div ref={setRef}/>
Тут "движок" реакта разберется когда сетнуть новую ссылку на элемент, а когда она осталась неизменной. Но все таки есть конечно слабые места, но пока до них не дошли, мне кажется, что первая "косячная" реализация хука более приятная, очевидная и менее запутанная
Callback рефы в React: что это такое и где можно применять