【问题标题】:React too many re-renders on if else statements对 if else 语句做出过多的重新渲染反应
【发布时间】:2021-03-23 00:58:53
【问题描述】:

每当程序注意到主题时,我都会尝试使用 setState 更改值 currentTheme,这是存储在 App.js 中的状态更改为特定值。我收到此错误:

Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.

不太确定在这里做什么,任何帮助将不胜感激。提前致谢!

   import React, { useState } from "react";
    import styled from "styled-components";
    import Editor from "./Editor";
    import { monokai, solarizedDark, terminal, textmate } from "./ui/themes";


const CodeContainer = ({ setHtml, setCss, setJs, setRenderDoc, theme }) => {
  const [currentTheme, setCurrentTheme] = useState(monokai);

  if (theme === "textmate") {
    setCurrentTheme(textmate);
  } else if (theme === "solarized_dark") {
    setCurrentTheme(solarizedDark);
  } else if (theme === "terminal") {
    setCurrentTheme(terminal);
  } else {
    setCurrentTheme(monokai);
  }

  return (
    <Container style={currentTheme}>
      <Editor
        setHtml={setHtml}
        setRenderDoc={setRenderDoc}
        languageName="html"
        theme={theme}
      />
      <Editor
        setCss={setCss}
        setRenderDoc={setRenderDoc}
        languageName="css"
        theme={theme}
      />
      <Editor
        setJs={setJs}
        setRenderDoc={setRenderDoc}
        languageName="javascript"
        theme={theme}
      />
    </Container>
  );
};

export default CodeContainer;

const Container = styled.div`
  height: 40vh;
  border-bottom: 4px #211e1c solid;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 15px;
`;

【问题讨论】:

  • 你需要将你的 if 语句包装在一个监听你的主题的 useEffect 中,以避免在每次渲染时运行 setCurrentTheme。

标签: javascript reactjs frontend rendering use-state


【解决方案1】:

我认为你不需要一个状态。做一个这样的函数,如果主题发生变化,它会为你得到正确的 currentTheme。

const getTheme = (theme) => {
  if (theme === "textmate")
      return textmate;
  if (theme === "solarized_dark") 
      return solarizedDark;
  if (theme === "terminal") 
      return terminal;
  return monokai;
}

这样称呼它:

<Container style={getTheme(theme)}>

或者,您可以使用局部变量内联计算主题:


const CodeContainer = ({ setHtml, setCss, setJs, setRenderDoc, theme }) => {
  let currentTheme = monokai;
  if (theme === "textmate") {
    currentTheme = textmate;
  } else if (theme === "solarized_dark") {
    currentTheme = solarizedDark;
  } else if (theme === "terminal") {
    currentTheme = terminal;
  }

  return (
    <Container style={currentTheme}>
    //...
  )
}

【讨论】:

  • @RyanHaraki 这个或类似的东西是最好的。原始问题中的 prop 和 state 是为了彼此保持同步而编写的,这是一个明确的信号,表明 prop 应该直接使用,而不是复制到 state 中。复制到状态会导致额外的代码复杂性,以及由于双重渲染导致的性能损失。
  • 好点,谢谢。代码工作得和你说的一样快,因为没有双重渲染。我会将此标记为最佳答案。
【解决方案2】:

正如评论中提到的,问题是您在每次渲染时都在运行它,并且反应会注意到并阻止应用程序运行。

您可以利用 useEffect 挂钩,该挂钩用于侦听值更改并根据这些更改执行副作用,它替换了基于类的组件中的所有生命周期方法。

React.useEffect(() => {
  if (theme === "textmate") {
    setCurrentTheme(textmate);
  } else if (theme === "solarized_dark") {
    setCurrentTheme(solarizedDark);
  } else if (theme === "terminal") {
    setCurrentTheme(terminal);
  } else {
    setCurrentTheme(monokai);
  }
}, [theme])

第一个参数是一个回调,它将在主题值更改时运行您想要执行的任何逻辑,第二个参数是一个数组,用于告诉 useEffect 何时运行,所以在这种情况下,我们通过主题道具,它会告诉钩子在每次值更改时运行。 如果您想了解更多可以做的事情,这是它的文档: https://reactjs.org/docs/hooks-effect.html

【讨论】: