Home > Blockchain >  React, Too many rerenders
React, Too many rerenders

Time:05-03

Given the following code:

export default function CrearContacto({setOpening}) {
    
      const [open, setOpen] = useState(false);
      setOpen(setOpening);
    
      useEffect(() => {
        console.log(setOpening);
        setOpen(setOpening);
      }, [setOpening]); 
    
      const handleClose = () =>{setOpening=false};
    
      return (
        <div>
          <Dialog open={open} onClose={handleClose}>
//code continues

I'm getting a Too many re-renders error, however I'm unable so see where is the loop here. {setOpening} is being used in a button in another component, when clicked, true is sent. setOpen sets new open state as true, so Dialog can pop-up. At the same time useEffect is watching setOpening for changes (probably this is also wrong, I'm trying to make this work without success). If closed, handleClose is triggered and now setOpening equals false, so useEffect detects the change and executes setOpen to false. I don't see the loop, but also the code isn't working as intended.

Edit - Added parent relative code

function Agenda() {
 const MiContexto = useOutletContext();
 return (<CrearContacto setOpening={MiContexto.modalState}/>
//code continues

MainLayout class:

 import Navbar from "./Navbar"
    import Topbar from "./Topbar"
    import React, {useReducer} from "react";
    import { Outlet, useOutletContext } from "react-router-dom";
    
    export const MainContexto = React.createContext({modalState:false});
    
    const initialState=false;
    
    const estadosPosiblesModal = (state, action) =>{
      switch(action){
        case false:
          return false;
        case true:
          return true;
        default: 
          return state;
      }
    }
    const MainLayout = ()=> {
    const [EstadoModal, dispatch] = useReducer(estadosPosiblesModal,initialState);
    
      return (
        <MainContexto.Provider value={{modalState:EstadoModal,modalDispatch:dispatch}}>
          <div className="mainlayout">
            <Navbar/>
            <main className="mainlayout_topPlusContent">
              <Topbar className="TopBar" prueba={EstadoModal}/>
              <Outlet context={{modalState:EstadoModal,modalDispatch:dispatch}}/>
            </main>
          </div>
        </MainContexto.Provider>
      );
        
    };
    
      export default MainLayout;

I had to use simultaneously a custom context and the Router outlet context as my context wasn't going through the outlet.

Topbar contains:

  const micontext = useContext(MainContexto);
...
        <Button variant="contained" onClick={()=>micontext.modalDispatch(true)}>

CodePudding user response:

Your problem is you call setOpen(setOpening) on the component level CrearContacto which is causing the infinite re-renderings on that component. You can imagine this cycle: state update (setOpen(setOpening)) > re-rendering > call state update again > ...

And another problem is you cannot set setOpening=false directly, it needs to be set with a state setter from the parent component.

export default function CrearContacto({setOpening}) {
      
      const [open, setOpen] = useState(setOpening);
      //setOpen(setOpening); //remove this part

      useEffect(() => {
         setOpen(setOpening)
      },[setOpening])
    
      const handleClose = () =>{setOpen(false)}; //set `open` state internally instead
    
      return (
        <div>
          <Dialog open={open} onClose={handleClose}>
        </div>)
}

If you want to update the parent component's state, you should pass the state setter and state value to your child component which is CrearContacto too

I'd assume that you have this state on the parent component

const [opening, setOpening] = useState(false);

Note that setOpening is following the naming convention of the state setter, so I'd prefer to do it this way instead

You can try to modify your component like below

export default function CrearContacto({setOpening, opening}) {
      
      const [open, setOpen] = useState(opening);
      //setOpen(setOpening); //remove this part

      useEffect(() => {
         setOpen(opening)
      },[opening])
    
    
      const handleClose = () =>{setOpening(false)};
    
      return (
        <div>
          <Dialog open={open} onClose={handleClose}>
        </div>)
}

If you only use open state for Dialog, you can remove that state declaration too

export default function CrearContacto({setOpening, opening}) {
    
      const handleClose = () =>{setOpening(false)};
    
      return (
        <div>
          <Dialog open={opening} onClose={handleClose}>
        </div>)
}

CodePudding user response:

To expand my comment:

  • From inside (or a child) component, you can change the open value by using the state, so call the setOpen function.
  • From a parent component you can change the value of open by changing the prop setOpening (rename this prop as it sounds like it's a function), so you would have:
export default function CrearContacto({setOpening}) {
    
      const [open, setOpen] = useState(setOpening);
    
      useEffect(() => {
        console.log(setOpening);
        setOpen(setOpening);
      }, [setOpening]); 
    
      const handleClose = () =>{setOpen(false)};
    
      return (
        <div>
          <Dialog open={open} onClose={handleClose}>
  • Related