反应从DOM计算的初始状态

时间:2019-02-01 11:21:30

标签: reactjs typescript react-hooks

我正在使用https://www.npmjs.com/package/react-slick,并且尝试根据可用宽度动态地将其设置为“ slidesToShow”。

我设法使其正常运行,但仅在调整窗口大小之后才可以。我需要提供一个根据元素宽度计算出的初始状态。问题在于该元素当时不存在。

这是我的代码(有趣的部分):

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;

如果我在updateSettings中调用useEffect,则会遇到无限循环。

所以我必须:

  • 以某种方式获得尚未创建的元素的宽度
  • 在第一次渲染后调用一次updateSettings。

2 个答案:

答案 0 :(得分:1)

您可以有一个返回maxItems并可以在各处使用的函数,所以:

const getMaxItems = () => Math.round(slider.current.innerSlider.list.parentElement.clientWidth / 136)

您可以在recalculateItemsShown中使用其返回结果来更新设置。

const recalculateItemsShown = () => {
  updateSettings({...props.settings, slidesToShow: getMaxItems()});
};

您还可以使用其返回值初始设置状态。

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

答案 1 :(得分:1)

我相信uselayouteffect挂钩是针对这种确切的用例而设计的。

https://reactjs.org/docs/hooks-reference.html#uselayouteffect

它的功能与useEffect完全相同,除了它会在dom加载后触发,以便您可以根据需要计算元素宽度。