使用 React Hooks 获得“太多重新渲染”错误

IT技术 reactjs react-hooks graphql apollo-client use-state
2022-07-15 02:13:14

我在这里为 React Hooks 苦苦挣扎。我在网上查看,但不知道如何使示例适应我的代码。我有以下组件会触发“重新渲染过多”错误:

const EmailVerification = () => {
  const [showMessage, setShowMessage] = useState(true);
  const [text, setText] = useState("...Loading. Do not close.");

  const { data, error } = useQuery(VERIFY_EMAIL);
  if (error) {setText(genericErrorMessage);}
  if (data) {setText(emailVerificationMessage);}

  return (
    <Wrapper>
      <Message setShowMessage={setShowMessage} text={text} />
    </Wrapper>
  )
}

如何重新组织我的代码以避免此错误?我知道 useEffect 钩子应该用于执行副作用,尽管我不知道在这种情况下如何使用它(假设它是必要的)。

2个回答

触发错误是因为您setText直接在渲染函数中使用。该函数在调用后渲染组件。因为在下一次渲染中,data并且error仍然设置,它setText再次调用。

你是对的useEffectuseEffect您可以确保仅在数据发生更改时才调用该函数setText在您的情况下,这是用于data和/或error变量。

import { useEffect } from 'react';

const EmailVerification = () => {
  const [showMessage, setShowMessage] = useState(true);
  const [text, setText] = useState("...Loading. Do not close.");

  const { data, error } = useQuery(VERIFY_EMAIL);
  
  useEffect(() => {
    if (error) setText('message');
    if (data) setText('emailVerificationMessage');
  }, [error, data]);
  
  return (
    <Wrapper>
      <Message setShowMessage={setShowMessage} text={text} />
    </Wrapper>
  )
}

但是,由于您只是text使用现有的props更改变量,因此您也可以仅在 JS(X) 中执行此操作:


const EmailVerification = () => {
  const [showMessage, setShowMessage] = useState(true);
  const { isLoading, data, error } = useQuery(VERIFY_EMAIL);
  
  const text = isLoading ? 'Loading... Do not close' : error || !data ? 'Error message' : 'emailVerificationMessage';

  return (
    <Wrapper>
      <Message setShowMessage={setShowMessage} text={text} />
    </Wrapper>
  )
}

这使用可以用任何其他方法替换的嵌套三元运算符(不是风扇)。

setText将导致重新渲染,并将在下一次渲染时再次调用。据我了解,您希望在查询返回错误或数据后设置文本。

为避免这种情况,请使用 onError 和 onCompleted ,您可以像这样传递给 useQuery :

const { data, error } = useQuery(VERIFY_EMAIL, {
  onCompleted: () => setText(emailVerificationMessage),
  onError: () => setText(genericErrorMessage)
});

并删除这两行:

if (error) {setText(genericErrorMessage);}
if (data) {setText(emailVerificationMessage);}

或在 useEffect 中调用 setText:

useEffect(() => {
  if (error) {
    setText(genericErrorMessage)
  }
}, [error])