Это относительно простая проблема, но я не смог ее решить. Я построил следующую карусель / слайдер:

const BlogPostCardSlider = ({ children }) ={amp}gt; { const [activeSlide, setActiveSlide] = useState(0); const activeSlideRef = useRef(null); const wrapperRef = useRef(null); const firstRenderRef = useRef(true); useEffect(() ={amp}gt; { if (firstRenderRef.current) { //this is checking whether its the first render of the component. If it is, we dont want useEffect to run. firstRenderRef.current = false; } else if (activeSlideRef.current) { activeSlideRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' }); } }, [activeSlide]); const moveRight = () ={amp}gt; { if (activeSlide   1 {amp}gt;= children.length) { return children.length - 1; } return activeSlide   1; }; const moveLeft = () ={amp}gt; { if (activeSlide - 1 {amp}lt;= 0) { return 0; } return activeSlide - 1; }; return ( {amp}lt;div id="trap" tabIndex="0"{amp}gt; {amp}lt;button onClick={() ={amp}gt; setActiveSlide(moveLeft)}{amp}gt;PREV{amp}lt;/button{amp}gt; {children.map((child, i) ={amp}gt; { return ( {amp}lt;SlideLink key={`slideLink-${i}`} isActive={activeSlide === i} onClick={e ={amp}gt; { setActiveSlide(i); }} {amp}gt; {i   1} {amp}lt;/SlideLink{amp}gt; ); })} {amp}lt;Wrapper onScroll={e ={amp}gt; { let { width } = wrapperRef.current.getBoundingClientRect(); let { scrollLeft } = wrapperRef.current; if ((scrollLeft / width) % 1 === 0) { setActiveSlide(scrollLeft / width); } }} ref={wrapperRef} {amp}gt; {children.map((child, i) ={amp}gt; { return ( {amp}lt;Slide key={`slide-${i}`} ref={i === activeSlide ? activeSlideRef : null} {amp}gt; {child} {amp}lt;/Slide{amp}gt; ); })} {amp}lt;/Wrapper{amp}gt; {amp}lt;button onClick={() ={amp}gt; setActiveSlide(moveRight)}{amp}gt;NEXT{amp}lt;/button{amp}gt; {amp}lt;/div{amp}gt; ); }; export default BlogPostCardSlider; 

Он отображает своих детей в виде слайдов в карусели. Вы можете перемещаться по карусели, нажимая кнопки «NEXT» или «PREVIOUS». Также есть компонент, который показывает, на каком слайде вы находитесь (например: 1 2 *3* 4 и т. Д.). Я называю это SlideLink(s) . Всякий раз, когда activeSlide обновляется, SlideLink будет обновлять и выделять вам новый текущий слайд.

Наконец, вы также можете перемещаться с помощью прокрутки. И вот проблема :

Всякий раз, когда кто-то прокручивает, я проверяю, на каком слайде он находится, выполняя некоторые вычисления:

 onScroll={e ={amp}gt; { let { width } = wrapperRef.current.getBoundingClientRect(); let { scrollLeft } = wrapperRef.current; if ((scrollLeft / width) % 1 === 0) { setActiveSlide(scrollLeft / width); } }} 

… результатом этого является то, что setActiveSlide вызывается только после полного просмотра слайда. Это приводит к запаздывающему опыту: пользователь перешел к следующему слайду, следующий слайд находится в поле зрения, но расчет еще не завершен (поскольку он завершается только после того, как слайд отображается на 100%), поэтому активная SlideLink обновляется очень поздно в процессе.

Как бы я решил это? Есть ли способ оптимистично обновить это?

С первого взгляда я подумал, что это должно быть быстрое решение. Оказалось, немного больше было необходимо. Я создаю рабочий пример здесь:

https://codesandbox.io/s/dark-field-g78zb?fontsize=14{amp}amp;hidenavigation=1{amp}amp;theme=dark

Суть в том, что setActiveSide следует setActiveSide не во время события onScroll , а, когда пользователь onScroll прокрутку. Это достигается в примере с использованием onIdle реакции onIdle .

Вы можете немного изменить расчеты, чтобы, когда слайд составлял более 50% в представлении, присваивал активному свойству это значение.

 onScroll={e ={amp}gt; { let { width } = wrapperRef.current.getBoundingClientRect(); let { scrollLeft } = wrapperRef.current; setActiveSlide(Math.round(scrollLeft / width)   1); }} 

Пример: Карусель с шириной: 800 пикселей, Всего слайдов: 3. Таким образом, scrollLeft будет варьироваться от 0 до 1600

  • От 0 до 400: активный слайд = Math.round (scrollLeft / width) = 0-й индекс (или первый слайд)
  • 400-1200: активный слайд = 1-й индекс (или второй слайд), так как он больше в представлении
  • От 1200-1600: Активный слайд = 2-й индекс (или третий слайд), так как он больше в представлении

Надеюсь, это поможет. Возврат к любым сомнениям.