反应从 DOM 计算的初始状态
react initial state calculated from DOM
我正在使用 https://www.npmjs.com/package/react-slick,我正在尝试根据可用宽度动态地设置 "slidesToShow"。
我设法让它工作,但只有在 window 调整大小之后。我需要为其提供根据元素宽度计算的初始状态。问题是该元素当时不存在。
这是我的代码(有趣的部分):
const Carousel = (props: ICarouselProps) => {
const slider = useRef(null);
const recalculateItemsShown = () => {
const maxItems = Math.round(
slider.current.innerSlider.list.parentElement.clientWidth / 136, // content width is just hardcoded for now.
);
updateSettings({...props.settings, slidesToShow: maxItems});
};
useEffect(() => {
window.addEventListener('resize', recalculateItemsShown);
return () => {
window.removeEventListener('resize', recalculateItemsShown);
};
});
const [settings, updateSettings] = useState({
...props.settings,
slidesToShow: //How do I set this properly? slider is null here
});
return (
<div id="carousel" className="carousel">
<Slider {...settings} {...arrows} ref={slider}>
<div className="test-content/>
<div className="test-content/>
/* etc. */
<div className="test-content/>
<div className="test-content/>
<div className="test-content/>
</Slider>
</div>
);
};
export default Carousel;
如果我在 useEffect
中调用 updateSettings
,我会得到一个无限循环。
所以我必须:
- 以某种方式获取尚未创建的元素的宽度
- 或
- 在第一次渲染后调用一次 updateSettings。
你可以有一个 returns maxItems
的函数并在任何地方使用它,所以:
const getMaxItems = () => Math.round(slider.current.innerSlider.list.parentElement.clientWidth / 136)
您在 recalculateItemsShown
内使用它的 return 结果来更新设置。
const recalculateItemsShown = () => {
updateSettings({...props.settings, slidesToShow: getMaxItems()});
};
并且您还使用它的 return 值来初始设置状态。
const [settings, updateSettings] = useState({
...props.settings,
slidesToShow: getMaxItems()
});
如果该元素最初不存在,您可以使用 useEffect
和一个空数组作为第二个参数。这告诉 useEffect
观察该数组的更改并在每次更改时调用它,但由于它是一个永远不会更改的空数组,因此它只会 运行 一次 - 在初始渲染时。
useEffect(() => {
updateSettings({...props.settings, slidesToShow: getMaxItems()});
}, []);
您可以在此处阅读有关跳过应用事件的更多信息:https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
我相信 uselayouteffect
钩子就是为这个确切的用例设计的。
https://reactjs.org/docs/hooks-reference.html#uselayouteffect
它的工作方式与 useEffect 完全相同,只是它在 dom 加载后触发,以便您可以根据需要计算元素宽度。
我正在使用 https://www.npmjs.com/package/react-slick,我正在尝试根据可用宽度动态地设置 "slidesToShow"。
我设法让它工作,但只有在 window 调整大小之后。我需要为其提供根据元素宽度计算的初始状态。问题是该元素当时不存在。
这是我的代码(有趣的部分):
const Carousel = (props: ICarouselProps) => {
const slider = useRef(null);
const recalculateItemsShown = () => {
const maxItems = Math.round(
slider.current.innerSlider.list.parentElement.clientWidth / 136, // content width is just hardcoded for now.
);
updateSettings({...props.settings, slidesToShow: maxItems});
};
useEffect(() => {
window.addEventListener('resize', recalculateItemsShown);
return () => {
window.removeEventListener('resize', recalculateItemsShown);
};
});
const [settings, updateSettings] = useState({
...props.settings,
slidesToShow: //How do I set this properly? slider is null here
});
return (
<div id="carousel" className="carousel">
<Slider {...settings} {...arrows} ref={slider}>
<div className="test-content/>
<div className="test-content/>
/* etc. */
<div className="test-content/>
<div className="test-content/>
<div className="test-content/>
</Slider>
</div>
);
};
export default Carousel;
如果我在 useEffect
中调用 updateSettings
,我会得到一个无限循环。
所以我必须:
- 以某种方式获取尚未创建的元素的宽度
- 或
- 在第一次渲染后调用一次 updateSettings。
你可以有一个 returns maxItems
的函数并在任何地方使用它,所以:
const getMaxItems = () => Math.round(slider.current.innerSlider.list.parentElement.clientWidth / 136)
您在 recalculateItemsShown
内使用它的 return 结果来更新设置。
const recalculateItemsShown = () => {
updateSettings({...props.settings, slidesToShow: getMaxItems()});
};
并且您还使用它的 return 值来初始设置状态。
const [settings, updateSettings] = useState({
...props.settings,
slidesToShow: getMaxItems()
});
如果该元素最初不存在,您可以使用 useEffect
和一个空数组作为第二个参数。这告诉 useEffect
观察该数组的更改并在每次更改时调用它,但由于它是一个永远不会更改的空数组,因此它只会 运行 一次 - 在初始渲染时。
useEffect(() => {
updateSettings({...props.settings, slidesToShow: getMaxItems()});
}, []);
您可以在此处阅读有关跳过应用事件的更多信息:https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
我相信 uselayouteffect
钩子就是为这个确切的用例设计的。
https://reactjs.org/docs/hooks-reference.html#uselayouteffect
它的工作方式与 useEffect 完全相同,只是它在 dom 加载后触发,以便您可以根据需要计算元素宽度。