reactjs 在每个组件渲染问题中,React Hooks必须以完全相同的顺序调用?

ee7vknir  于 2023-03-12  发布在  React
关注(0)|答案(1)|浏览(234)

我实现了一个项目列表中的键盘键或按钮导航。在本地我没有得到任何错误,在构建阶段我得到了以下错误:

Error: React Hook "useRef" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?  react-hooks/rules-of-hooks
  Error: React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?  react-hooks/rules-of-hooks
 Error: React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?  react-hooks/rules-of-hooks

这是我的代码:

'use client'
import { FixedSizeList } from 'react-window'
import useIsMobile from '../../../../../hooks/useIsMobile'
import PackagesCard from './packagesCard'
import AutoSizer from 'react-virtualized-auto-sizer'
import { useRef, useState, useEffect } from 'react'
import Image from 'next/image'

const VirtualPackagesCarousel = ({ data }) => {
  const { mobile } = useIsMobile()
  if (!data || data.length === 0) {
    return (
      <div className="h-[350px] flex text-neutrals-50 justify-center align-middle">
        There are no courses here yet!
      </div>
    )
  }
  const listRef = useRef(null)
  const [selection, setSelection] = useState(null)

  const handleKeyDown = (e) => {
    if (e.key === 'ArrowRight') {
      e.preventDefault()
      if (selection === null) {
        setSelection(0)
      } else {
        setSelection(Math.min(data.length - 1, selection + 1))
      }
    }

    if (e.key === 'ArrowLeft') {
      e.preventDefault()
      if (selection === null) {
        setSelection(0)
      } else {
        setSelection(Math.max(0, selection - 1))
      }
    }
  }

  const handleNext = () => {
    if (selection === null) {
      setSelection(0)
    } else {
      setSelection(Math.min(data.length - 1, selection + 1))
    }
  }

  const handleBack = () => {
    if (selection === null) {
      setSelection(0)
    } else {
      setSelection((prev) => Math.max(0, prev - 1))
    }
  }
  const handleBlur = () => {
    setSelection(null)
  }

  useEffect(() => {
    const list = listRef.current
    if (!list) return

    if (selection === null) return

    list.scrollToItem(selection)
  }, [selection])

  return (
    <div
      tabIndex={0}
      onKeyDown={handleKeyDown}
      onBlur={handleBlur}
      className="w-full flex flex-col gap-5 outline-none border-transparent focus:border-transparent focus:ring-0"
    >
      <div className="flex gap-3 justify-end visible mobile:invisible">
        <Image
          src="/images/arrowLeft.svg"
          alt="arrowBack"
          width="10"
          height="10"
          className="cursor-pointer invert"
          onClick={handleBack}
        />
        <Image
          src="/images/arrowRight.svg"
          alt="arrowNext"
          width="10"
          height="10"
          className="cursor-pointer invert "
          onClick={handleNext}
        />
      </div>

      <AutoSizer>
        {({ _, width }) => (
          <FixedSizeList
            height={400}
            itemCount={data.length}
            itemSize={mobile ? 300 : 600}
            width={width}
            layout="horizontal"
            ref={listRef}
            className="outline-none border-transparent focus:border-transparent focus:ring-0"
          >
            {({ index, style }) => (
              <div className="px-2.5 mobile:px-1" style={style}>
                <PackagesCard
                  data={data[index]}
                  key={data[index].id}
                  index={index}
                  setSelection={setSelection}
                  selection={selection}
                />
              </div>
            )}
          </FixedSizeList>
        )}
      </AutoSizer>
    </div>
  )
}

export default VirtualPackagesCarousel

我不知道我哪里出错了,有人能帮我弄清楚错误在哪里吗?为什么我在本地没有收到这些错误,而在构建阶段却收到了?

bvuwiixz

bvuwiixz1#

将第一个if语句移到所有钩子调用之后。参见Rules of Hooks
只要渲染器之间的Hook调用顺序相同,React就可以将某些本地状态与每个渲染器关联起来。

const { mobile } = useIsMobile();
const listRef = useRef(null);
const [selection, setSelection] = useState(null);
useEffect(() => {
    const list = listRef.current
    if (!list) return
    if (selection === null) return
    list.scrollToItem(selection)
}, [selection]);
if (!data || data.length === 0) {
    return (
      <div className="h-[350px] flex text-neutrals-50 justify-center align-middle">
        There are no courses here yet!
      </div>
    );
}

相关问题