reactjs 获取错误:渲染了比上一次渲染更多的钩子

lnxxn5zx  于 2023-02-15  发布在  React
关注(0)|答案(4)|浏览(136)

当我加载我的Nextjs页面时,我收到以下错误消息:* "错误:渲染的钩子比上次渲染时多。"*
如果我在useEffect代码之后添加if (!router.isReady) return null,页面在初始加载时就不能访问solutionId,从而导致useDocument钩子出错,该钩子需要solutionId从数据库中获取文档。
因此,this thread不能解决我的问题。
任何人,请帮助我解决这个问题!
我的代码:

const SolutionEditForm = () => {
  const [formData, setFormData] = useState(INITIAL_STATE)
  const router = useRouter()
  const { solutionId } = router.query

  if (!router.isReady) return null

  const { document } = useDocument("solutions", solutionId)
  const { updateDocument, response } = useFirestore("solutions")

  useEffect(() => {
    if (document) {
      setFormData(document)
    }
  }, [document])

  return (
    <div>
     // JSX code
    </div>
  )
}

useDocument挂钩:

export const useDocument = (c, id) => {
  const [document, setDocument] = useState(null)
  const [isLoading, setIsLoading] = useState(true)
  const [error, setError] = useState(null)

  useEffect(() => {
    const ref = doc(db, c, id)
    const unsubscribe = onSnapshot(
      ref,
      (snapshot) => {
        setIsLoading(false)
        if (snapshot.data()) {
          setDocument({ ...snapshot.data(), id: snapshot.id })
          setError(null)
        } else {
          setError("No such document exists")
        }
      },
      (err) => {
        console.log(err.message)
        setIsLoading(false)
        setError("failed to get document")
      }
    )
    return () => unsubscribe()
  }, [c, id])

  return { document, isLoading, error }
}
jtw3ybtb

jtw3ybtb1#

你不能在一个条件之后调用钩子useEffect,你的定制useDocument,或者任何其他的。在你的例子中的条件是这个早期返回的if (!router.isReady) returns null

不要在循环、条件或嵌套函数内调用Hooks。相反,始终在React函数的顶层使用Hooks,在任何早期返回之前...

只需将该if (!router.isReady) returns nullSolutionEditForm中删除,并按如下所示更改useDocument

export const useDocument = (c, id) => {
  const [document, setDocument] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    if (!id) return; // if there is no id, do nothing 👈🏽
    const ref = doc(db, c, id);
    const unsubscribe = onSnapshot(
      ref,
      (snapshot) => {
        setIsLoading(false);
        if (snapshot.data()) {
          setDocument({ ...snapshot.data(), id: snapshot.id });
          setError(null);
        } else {
          setError("No such document exists");
        }
      },
      (err) => {
        console.log(err.message);
        setIsLoading(false);
        setError("failed to get document");
      }
    );
    return () => unsubscribe();
  }, [c, id]);

  return { document, isLoading, error };
};
djp7away

djp7away2#

if (!router.isReady) return null语句导致函数提前结束,并且不执行后续钩子。
你需要重新构造钩子,使它们都不是条件钩子:

const [formData, setFormData] = useState(INITIAL_STATE)
  const router = useRouter()
  const { solutionId } = router.query

  const { document } = useDocument("solutions", solutionId, router.isReady) // pass a flag to disable until ready
  const { updateDocument, response } = useFirestore("solutions")

  useEffect(() => {
    if (document) {
      setFormData(document)
    }
  }, [document])

// Move this to after the hooks.
  if (!router.isReady) return null

然后使useDocument避免发送额外的呼叫:

export const useDocument = (c, id, enabled) => {

并使用复选框更新了效果:

useEffect(() => {
    if (!enabled) return;
    const ref = doc(db, c, id)
    const unsubscribe = onSnapshot(
      ref,
      (snapshot) => {
        setIsLoading(false)
        if (snapshot.data()) {
          setDocument({ ...snapshot.data(), id: snapshot.id })
          setError(null)
        } else {
          setError("No such document exists")
        }
      },
      (err) => {
        console.log(err.message)
        setIsLoading(false)
        setError("failed to get document")
      }
    )
    return () => unsubscribe()
  }, [c, id, enabled])
zujrkrfu

zujrkrfu3#

1.无法有条件地调用UseEffect
1.仅在客户端调用UseEffect。
如果您使用最小表示法,可以尝试修复此错误

ztigrdn8

ztigrdn84#

路由器就绪是好的,但您需要等待查询参数。请尝试以下操作:

import { useRouter } from 'next/router'

const useQuery = () => {
  const router = useRouter()

  const ready = router.asPath !== router.route
  if (!ready) return null
  return router.query
}

/// page.tsx
const query = useQuery()
useEffect(() => {
   if (!query) {
     return
   }

   //your code 

}, [query])

相关问题