【问题标题】:useState and if statement causing Error: Too many re-renders. React limits the number of renders to prevent an infinite loopuseState 和 if 语句导致错误:重新渲染过多。 React 限制渲染次数以防止无限循环
【发布时间】:2021-02-06 00:41:59
【问题描述】:

我正在尝试创建的是一个 React 组件,它在 prop boxToggle 的值为 true 时有条件地呈现,并且在用户单击退出按钮时也返回 null。我为实现这一点所做的是创建一个名为 displayUI 的本地 useState 并使用 if 语句将 displayUI 分配为 true 或 false(这可能是问题,但我无法确定原因),具体取决于 boxToggle 道具和 onClick 内部退出按钮。

但我在组件内部收到此错误:

未捕获的错误:重新渲染过多。 React 限制数量 渲染以防止无限循环。

这里是父组件(我使用 react-three-fiber)

import React, { useRef, useState } from 'react';
import './App.css';

//Packages
import * as THREE from 'three';
import { Canvas, useFrame } from 'react-three-fiber'



import {Box, OrbitControls} from 'drei';


//Components
import Header from './components/Header';
import Homepage from './components/Homepage';
import { Light } from 'three';

//Functions

  //Main App Function
function App() {
  

  const [boxToggle, setboxToggle] = useState(false);  

  return (
    <div className="App">
      <Header/>
      <Homepage boxToggle={boxToggle}/>
      <Canvas
        colorManagement 
        camera={{position: [0, 5, 10], fov: 60}}
        shadowMap
      >
        <Lights/>
        <RotatingBox
          onClick={()=> setboxToggle(true)}
        />
        <Floor/>
        {/* <OrbitControls/> OrbitControls causes problems with the Raycasting (onClick)*/}
      </Canvas>
    </div>
  );
}

  //Scene Lights
const Lights = () => {

  return (
    <>
      <directionalLight
        intensity={1.5}
        position={[0, 10, 5]}
        castShadow
        shadow-mapSize-width={1024}
        shadow-mapSize-height={1024}
        shadow-camera-far={50}
        shadow-camera-left={-10}
        shadow-camera-right={10}
        shadow-camera-top={10}
        shadow-camera-bottom={-10}
      />
      <ambientLight
        intensity={0.3}
      />
    </>
  )
}

  //Meshes
const RotatingBox = ({onClick})=> {

  const box = useRef();

  const [boxSize, setBoxSize] = useState(false);  

  useFrame(()=>(
    box.current.rotation.y = box.current.rotation.x += 0.01,
    console.log(boxSize)
    )
  )

  return (
      <>
        <Box
          args={ boxSize ? [1, 1, 1]: [2, 2, 2]}
          ref={box}
          castShadow
          onClick={onClick}//cant get onClick Working at all, drei or R3F
          onPointerOver={onClick= ()=> setBoxSize(true)}
        >
          <meshStandardMaterial 
            
            cast
            attach="material" 
            color='yellow'
          />
        </Box>
      </>
  )
}

const Floor = () => {
  return (
    <>
      <mesh
        onClick={()=> console.log("click")} 
        receiveShadow 
        rotation={[-Math.PI / 2, 0, 0]} 
        position={[0, -3, 0]}
      >
        <planeBufferGeometry attach='geometry' args={[100, 100]}/>
        <meshStandardMaterial opacity={1} attach='material'/>
      </mesh>
    </>
  )
}

export default App;

和子主页组件

import React, { useState } from 'react';
import styles from './Homepage.module.css'

const Homepage = ({boxToggle}) => {

    const [displayUI, setDisplayUI] = useState(false);

    if (boxToggle) { 
        setDisplayUI(true);
    }


    if (displayUI === true) {
        return (
            <div className={styles.container}>
                <button onClick={() => setDisplayUI(false)} className="exit">X</button>
                <div className={styles.title}>
                    <h1>Homepage</h1>
                </div>
                <div className={styles.menu}>
                    <h3>Option 1</h3>
                    <h3>Option 2</h3>
                    <h3>Option 3</h3>
                </div>
            </div>
        )
    } else return null
}

export default Homepage

我尝试了其他 stackoverflow 答案,但我无法将它们应用于我的。它们中的大多数与useEffect有关,或者它们缺少箭头功能。这让我感到困惑,我什至不知道从哪里开始,因为我不熟悉 Hooks。谢谢

【问题讨论】:

  • useState 中的 Homepage 在做什么?摆脱它,直接检查boxToggle
  • 这就是我最初所做的,但我也希望主页通过退出按钮关闭。你有什么解决办法?感谢您的回答。
  • 你可以为这个问题设置一个codepen吗?

标签: javascript reactjs if-statement react-hooks use-state


【解决方案1】:

我认为问题是由您的主页组件引起的。

const Homepage = ({boxToggle}) => {

    const [displayUI, setDisplayUI] = useState(false);

    //this generate error because it update the state directly when render
    /*if (boxToggle) {   
        setDisplayUI(true);
    }*/

    //we can fix it by wrap it with a useEffect hook so this effect only trigger when the boxToggle value change
    
    React.useEffect(()=>{
       if (boxToggle) {  
        setDisplayUI(true);
       }
   
    }, [boxToggle])  // <--- dependency that trigger the function call


    if (displayUI === true) {
        return (
            <div className={styles.container}>
                <button onClick={() => setDisplayUI(false)} className="exit">X</button>
                <div className={styles.title}>
                    <h1>Homepage</h1>
                </div>
                <div className={styles.menu}>
                    <h3>Option 1</h3>
                    <h3>Option 2</h3>
                    <h3>Option 3</h3>
                </div>
            </div>
        )
    } else return null
}

【讨论】:

  • 哇!而已!非常感谢您提供的快速且内容丰富的信息。我需要更多地了解使用 useEffect 的反应生命周期非常感谢您
  • 对不起。还有一件事......它只工作一次(就像我退出主页,然后再试一次没有任何反应),你知道为什么吗?
  • 如果 Homepage 组件仍然加载,该效果只会在依赖值 (boxToggle) 发生变化时触发。根据您的逻辑,如果 boxToggle 从 true 更改为 false 并点击您的条件块不执行任何操作,它将触发。您可以删除依赖数组 tho(第二个参数)以使其每次都触发。不推荐。
  • 而您的 setboxToggle(true)} /> 永远不会将 boxToggle 设置回 false。尝试执行 setboxToggle(!boxToggle) 以“切换”值并触发效果
  • 我相信我得到了你想要达到的目标。我认为您应该将 boxToggle 状态值设置为从 0 开始,然后每次点击旋转框时将 boxToggle 值加一。这样,它不会在初始化时显示主页 ui,但会在您点击旋转框时将 display 设置为 true。
猜你喜欢
  • 2021-10-21
  • 2021-09-05
  • 2020-09-17
  • 1970-01-01
  • 1970-01-01
  • 2020-04-05
  • 2021-12-25
  • 2021-11-18
  • 2021-07-01
相关资源
最近更新 更多