#reactjs #react-hooks
#reactjs #реагирующие хуки
Вопрос:
У меня есть следующий компонент:
// component.js
import React from 'react';
import useComponentSize from 'useComponentSize';
const component = () => {
const [comSize, comRef] = useComponentSize();
return (
<div style={{width: '100%'}} ref={comRef}>
<p>hi</p>
</div>
);
};
который использует useComponentSize
, сделанный мной хук:
// useComponentSize.js
import {
useRef,
useState,
useLayoutEffect,
} from 'react';
const useComponentSize = () => {
const [size, setSize] = useState({
width: 0,
height: 0,
});
const resizeRef = useRef();
useLayoutEffect(() => {
setSize(() => ({
width: resizeRef.current.clientWidth,
height: resizeRef.current.clientHeight,
}));
});
return [size, resizeRef];
};
export default useComponentSize;
но по какой-то причине, которую я не могу понять, она всегда превышает максимальную глубину обновления. Я попытался useLayoutEffect
зависеть от resizeRef
, что, как я думал, сработает, но затем он снова не обновляется (что, по размышлении, именно так, как я должен был ожидать, что ссылка будет работать).
Что я должен сделать, чтобы это работало правильно, и, самое главное, почему вышеупомянутое вызывает бесконечный цикл?
Редактировать: вторая попытка с использованием прослушивателей событий, по-прежнему неудачная. Какую концепцию я здесь упускаю?
// component.js
import React, { useRef } from 'react';
import useComponentSize from 'useComponentSize';
const component = () => {
const ref = useRef();
const [comSize] = useComponentSize(ref);
return (
<div style={{width: '100%'}} ref={ref}>
<p>hi</p>
</div>
);
};
import {
useRef,
useState,
useLayoutEffect,
} from 'react';
const useComponentSize = (ref) => {
const [size, setSize] = useState();
useLayoutEffect(() => {
const updateSize = () => {
setSize(ref.current.clientWidth);
}
ref.current.addEventListener('resize', updateSize);
updateSize();
return () => window.removeEventListener('resize', updateSize);
}, []);
return [size];
};
export default useComponentSize;
Это редактирование выше основано на этом useWindowSize
хуке, который отлично работает (в настоящее время я использую его в качестве замены, хотя я бы предпочел, чтобы вышеприведенное работало, и особенно чтобы знать, почему это не работает).
Небольшое объяснение того, чего я пытаюсь достичь, поскольку ранее это не было четко указано: я хочу, чтобы состояние size
обновлялось всякий раз, когда изменяется размер ссылочного компонента. То есть, если окно изменяется, а компонент остается того же размера, он не должен обновляться. Но если размер компонента действительно изменяется, то size
состояние должно изменить значение, чтобы отразить это.
Комментарии:
1. Вы пробовали
useEffect
вместоuseLayoutEffect
?2. Только что пробовал — без кубиков
3. @pilchard Я думаю, что это используется для уничтожения. Позвольте мне еще раз проверить документы!
4. @Yousaf, я тоже пробовал ваш метод, и он обновляется только в первый раз. Он не обновляется при изменении размера компонента!
5. Это решение не отслеживает изменения в элементах DOM. Вы можете использовать
ResizeObserver
, чтобы получить начальный размер, а также наблюдать за изменениями. Попробуйте эту демонстрацию
Ответ №1:
Ваш код застревает в бесконечном цикле, потому что вы не передали массив зависимостей в useEffectLayout
hook.
На самом деле вам вообще не нужно использовать useEffectLayout
hook. Вы можете наблюдать за изменениями в элементе DOM с помощью ResizeObserver API.
P.S: Хотя проблема OP уже решена с помощью демонстрации, опубликованной в одном из комментариев под вопросом, я публикую ответ для всех, кто может рассмотреть этот вопрос в будущем.
Пример:
const useComponentSize = (comRef) => {
const [size, setSize] = React.useState({
width: 0,
height: 0
});
React.useEffect(() => {
const sizeObserver = new ResizeObserver((entries, observer) => {
entries.forEach(({ target }) => {
setSize({ width: target.clientWidth, height: target.clientHeight });
});
});
sizeObserver.observe(comRef.current);
return () => sizeObserver.disconnect();
}, [comRef]);
return [size];
};
function App() {
const comRef = React.useRef();
const [comSize] = useComponentSize(comRef);
return (
<div>
<textarea ref={comRef} placeholder="Change my size"></textarea>
<h1>{JSON.stringify(comSize)}</h1>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Комментарии:
1. Вы полностью правы в том, что
useLayoutEffect
я предполагал, что мне нужноuseEffect
отслеживать изменения, иuseLayoutEffect
поскольку это необходимо, сначала отобразите ссылку. В частности, спасибо за исправление и указание мне на наблюдателя