reactjs - Popup : Showing a popup withouth clicking a button

1.5k views Asked by At

Im tying to show up a pop up whit the following code :

const [showPopup, setShowPopup] = useState(false);

Im handling the show/setpopup by this way :

 <Popup open={showPopup} onClose={() => setShowPopup(false)} modal>
    <span> Popup content </span>
  </Popup>


  {meta.error === 'codigo 2 fatores incorreto' ? (
    setShowPopup(true)
  ) : (
    <Popup style={{ visibility: "hidden" }}>.</Popup>
  )}

When it drops in the case (meta.error === 'codigo 2 fatores incorreto') he drops in a loop with the following eror : (Too many re-renders. React limits the number of renders to prevent an infinite loop. ) , someone knows how to solute it ?
I used this doc https://react-popup.elazizi.com/component-api/

whole component [WORKING] :

import React, { useState, useEffect } from 'react';
import { ErrorMessage, useField } from "formik";
import { StyledTextInput, StyledLabel, StyledIcon, ErrorMsg } from "./Styles";

// Eye for password
import { FiEyeOff, FiEye } from "react-icons/fi";

//pop up style.css 
import '../assets/css/popup.css'
// Import popup lib
import Popup from "reactjs-popup";
import 'reactjs-popup/dist/index.css';


function MyComponent() { 
  const [state, setState] = useState();
  setState(true);
  return (
    <Popup model
    trigger={open => <MyComponent open={open} />}
    position="right center"
    closeOnDocumentClick
  >
    <span> Popup content </span>  </Popup>
  );
}



export const TextInput = ({ icon, ...props }) => {
  const [field, meta] = useField(props);
  const [showpass, setShowpass] = useState(false);
  const [showPopup, setShowPopup] = useState(false);


  useEffect(() => {
    if(meta.error === 'codigo 2 fatores incorreto'){
    setShowPopup(true);
    }
  }, [meta.error])
  

  return (
    <div style={{ position: "relative" }}>
      <StyledLabel htmlFor={props.name}>{props.label}</StyledLabel>
      {props.type !== "password" && (
        <StyledTextInput
          invalid={meta.touched && meta.error}
          {...field}
          {...props}
        />
      )}
      {props.type === "password" && (
        <StyledTextInput
          invalid={meta.touched && meta.error}
          {...field}
          {...props}
          type={showpass ? "text" : "password"}
        />
      )}
      <StyledIcon>{icon}</StyledIcon>
      {props.type === "password" && (
        <StyledIcon onClick={() => setShowpass(!showpass)} right>
          {showpass && <FiEye />}
          {!showpass && <FiEyeOff />}
        </StyledIcon>
      )}
      {meta.touched && meta.error ? (
        <ErrorMsg>{meta.error}</ErrorMsg>
      ) : (
        <ErrorMsg style={{ visibility: "hidden" }}>.</ErrorMsg>
      )}

      <Popup open={showPopup} onClose={() => setShowPopup(false)} modal>
      {close => (
      <div className="modal">
        <button className="close" onClick={close}>
          &times;
        </button>

        
             
      </div>
    )}
    
  </Popup>
    {meta.error === "codigo 2 fatores incorreto" ? (
      !showPopup ? ( setShowPopup(true)) : ("") // <-- Only set state if not true 
 ) : <Popup>.</Popup>}
    </div>
  );
};
2

There are 2 answers

5
airi On BEST ANSWER

If I'm reading this right, it seems whenever meta.error matches your string, it'll constantly call setShowPopup(true) because the state updated - and calling that function causes the re-render, during which I assume meta.error is still 'codigo 2 fatores incorreto'.

I believe you could do something like the following to stop the re-rendering.

{meta.error === "codigo 2 fatores incorreto" ? (
    !showPopup ? setShowPopup(true) : "" // <-- Only set state if not true
) : (
    <Popup style={{visibility: "hidden"}}>.</Popup>
)}

I may be wrong though, and I may be misunderstanding the snippet.

2
juliomrc On

We should never ever use a setState inside the components render method. For class components, that is inside the render(), for function components, that is anywhere inside return() or in the component body, like here:

function MyComponent() { 
   const [state, setState] = useState();
   setState(true);
   return (...);
}

This will always cause an infinite loop.

  1. setState() triggers re-render.
  2. Re-render runs the component code again and triggers setState(). Go back to 1.

React provides tools to handle your case, such as useEffect.

Instead of

{meta.error === "codigo 2 fatores incorreto" ? (
        setShowPopup(true)
      ) : (
        <Popup style={{ visibility: "hidden" }}></Popup>
      )}

You should have

export const TextInput = ({ icon, ...props }) => {
  ...
  useEffect(() => {
    if(meta.error){
      setShowPopup(true);
    }
  }, [meta.error])
  
  return (
    ...
    <Popup style={{visibility: "hidden"}}>.</Popup>
  );