反应从 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,我会得到一个无限循环。

所以我必须:

你可以有一个 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 加载后触发,以便您可以根据需要计算元素宽度。