获取错误:渲染时使用了比之前更多的钩子

huangapple go评论62阅读模式
英文:

Getting Error: Rendered more hooks than during the previous render

问题

当我加载我的Next.js页面时,我会收到这个错误消息: "Error: Rendered more hooks than during the previous render."

如果我在useEffect代码之后添加if (!router.isReady) return null,页面在初始加载时无法访问solutionId,从而导致useDocument钩子出现错误,该钩子需要solutionId来从数据库中获取文档。

因此,这个帖子没有解决我的问题。

请帮我解决这个问题!

我的代码:

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 }
}
英文:

When I load my Nextjs page, I get this error message: "Error: Rendered more hooks than during the previous render."

If I add that if (!router.isReady) return null after the useEffect code, the page does not have access to the solutionId on the initial load, causing an error for the useDocument hook, which requires the solutionId to fetch the document from the database.

Therefore, this thread does not address my issue.

Anyone, please help me with this issue!

My code:

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

  if (!router.isReady) return null

  const { document } = useDocument(&quot;solutions&quot;, solutionId)
  const { updateDocument, response } = useFirestore(&quot;solutions&quot;)

  useEffect(() =&gt; {
    if (document) {
      setFormData(document)
    }
  }, [document])

  return (
    &lt;div&gt;
     // JSX code
    &lt;/div&gt;
  )
}

useDocument hook:

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

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

  return { document, isLoading, error }
}

答案1

得分: 1

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

  useEffect(() =&gt; {
    if (!id) return; // 如果没有id,什么也不做 &#128072;&#127997;
    const ref = doc(db, c, id);
    const unsubscribe = onSnapshot(
      ref,
      (snapshot) =&gt; {
        setIsLoading(false);
        if (snapshot.data()) {
          setDocument({ ...snapshot.data(), id: snapshot.id });
          setError(null);
        } else {
          setError("找不到该文档");
        }
      },
      (err) =&gt; {
        console.log(err.message);
        setIsLoading(false);
        setError("获取文档失败");
      }
    );
    return () =&gt; unsubscribe();
  }, [c, id]);

  return { document, isLoading, error };
};
英文:

You cannot call a hook, useEffect, your custom useDocument, or any other after a condition. The condition in your case is this early return if (!router.isReady) returns null. As you can read on Rules of Hooks:
>Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns...

Just remove that if (!router.isReady) returns null from SolutionEditForm and change useDocument as below.

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

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

  return { document, isLoading, error };
};

答案2

得分: 1

`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) // 传递一个标志以在准备就绪之前禁用
const { updateDocument, response } = useFirestore("solutions")

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

// 将此部分移到钩子之后。
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("文档不存在")
        }
      },
      (err) => {
        console.log(err.message)
        setIsLoading(false)
        setError("获取文档失败")
      }
    )
    return () => unsubscribe()
  }, [c, id, enabled])
英文:

The if (!router.isReady) return null statement caused the function to end early, and subsequent hooks are not executed.

You need to restructure your hooks such that none of them are conditional:

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


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

  useEffect(() =&gt; {
    if (document) {
      setFormData(document)
    }
  }, [document])

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

and then to make useDocument avoid sending extra calls:

export const useDocument = (c, id, enabled) =&gt; {

and updated the effect with a check:

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

答案3

得分: 0

  1. UseEffect 不能被有条件地调用。
  2. UseEffect 只在客户端被调用。

如果你提供最小的表示,可能会尝试修复这个错误。

英文:
  1. UseEffect cannot be called conditionally
  2. UseEffect is called only on the client side.

If you make minimal representation, possible to try fix this error

huangapple
  • 本文由 发表于 2023年2月14日 19:02:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/75446892.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定