首页 > 解决方案 > 对合成 onwheel 事件做出反应的上下文 api 状态不持久

问题描述

我正在尝试为轮播实现 onWheel 触发的导航。按钮导航有效,而 onWheel 触发,但不知何故未访问上下文提供程序的初始化状态。任何见解将不胜感激。谢谢你。

上下文提供者:

import CarouselContext from "../context/_carousel"

const CarouselProvider = ({ children }) => {
  const [isInitialized, setIsInitialized] = useState(false)
  const [carouselLength, setCarouselLength] = useState(0)
  const [carouselPosition, setCarouselPosition] = useState(0)

  const initializeCarousel = length => {
    setCarouselLength(length)
    setIsInitialized(true)
    console.log(`carouselLength ${carouselLength}`)
  }

  const nextSlide = () => {
    if (carouselPosition === carouselLength) {
      setCarouselPosition(0)
    } else {
      setCarouselPosition(carouselPosition + 1)
    }
    console.log(`carouselPosition ${carouselPosition}`)
  }

  const previousSlide = () => {
    if (carouselPosition === 0) {
      setCarouselPosition(carouselLength)
    } else {
      setCarouselPosition(carouselPosition - 1)
    }
    console.log(`carouselPosition ${carouselPosition}`)
  }

  const state = { carouselLength, carouselPosition, isInitialized }
  const methods = { initializeCarousel, nextSlide, previousSlide }

  return (
    <CarouselContext.Provider value={[state, methods]}>
      {children}
    </CarouselContext.Provider>
  )
}

export default CarouselProvider

轮播结构:

return (
    <Page className="works">
      <CarouselProvider>
        <ScrollNav>
          <PreviousWorkButton />
          <Carousel>
            {works.map((work, index) => (
              <CarouselItem key={index}>
                <Work project={work} />
              </CarouselItem>
            ))}
          </Carousel>
          <NextWorkButton />
        </ScrollNav>
      </CarouselProvider>
    </Page>
  )

scroll Nav(用于安慰事件被触发,但不显示轮播的当前位置或长度)

const ScrollNav = ({ children }) => {
  const [, { nextSlide, previousSlide }] = useContext(CarouselContext)

  const delayedScroll = useCallback(
    debounce(e => changeNav(e), 500, { leading: true, trailing: false }),
    []
  )
  const changeNav = direction => {
    if (direction === 1) {
      nextSlide()
    }
    if (direction === -1) {
      previousSlide()
    }
  }
  const onWheel = e => {
    delayedScroll(e.deltaY)
  }

  return <div onWheel={onWheel}>{children}</div>
}

onclick 按钮触发相同事件,轮播位置和长度保持不变

const NextWorkButton = () => {
  const [, { nextSlide }] = useContext(CarouselContext)

  const clicked = () => {
    nextSlide()
  }

  return (
    <div className="next-work-button">
      <button onClick={clicked}>
        <DownArrowSvg />
      </button>
    </div>
  )
}

编辑为在提供程序中添加 console.logs,就像在我的本地副本中一样

控制台登录点击事件:

carouselPosition 1
carouselLength 5 

控制台登录轮事件(长度不打印):

carouselPosition 0

感谢 kumarmo2,我通过消除去抖动并直接调用事件解决了这个问题。我使用计时器针对车轮事件进行了非常hacky的去抖动。我的解决方案:

const ScrollNav = ({ children }) => {
  const [, { nextSlide, previousSlide }] = useContext(CarouselContext)
  const [debounce, setDebounce] = useState(false)

  const timer = () => {
    setTimeout(() => {
      setDebounce(false)
    }, 1000)
  }

  const changeNav = e => {
    let direction = e.deltaY
    if (debounce) {
      return
    } else if (direction >= 1) {
      setDebounce(true)
      timer()
      nextSlide()
      return
    } else if (direction <= -1) {
      setDebounce(true)
      timer()
      previousSlide()
      return
    }
  }

  return <div onWheel={changeNav}>{children}</div>
}

标签: javascriptreactjsmousewheelreact-context

解决方案


我认为这里发生的事情是,delayedScroll由于useCallback. 它“捕获” ,changeNav而后者又捕获nextSlide.

所以 nextSlide 将被调用,但由于delayedScrolluseCallback 导致其内部引用未更新,因此您将面临问题。

您可以尝试删除useCallbackanddebounce一次delayedScroll吗?如果它有效,将以正确的方式引入去抖动逻辑。


推荐阅读