React setState hook keeps resetting and rendering the page twice

Viewed 189

I have an react app Form component split into Login and Signup forms. It is supposed to render the Signup by default but switch to Login if login is button is clicked. When login button is clicked, the page switches to the Login form very briefly before switching back to the Signup form. I don't know what is causing this. I have tried placing const [page, setPage] = setState("signup") in the parent App and passing setPage as a prop along with page. This produced the same results. I believe this issue is similar to this one but that was not resolved. Here is the app:

import Form from "./components/Signup-Form.js";
function App() {
    return (
        <div className="App">
            <h1>Welcome</h1>
            <Form />
        </div>
    );
}

export default App;

and Signup-Form.js:

import React from "react";
import { useState, useEffect } from "react";
import "./Forms.css";
import { InputField, Buttons } from "./Inputs";
function Form() {
    const [page, setPage] = useState("signup");
    const pageLabel = page;

    let Signup = () => {
        function toLogin() {
            setPage("login");
        }
        return (
            <form action="" method="get" className="form">
                <div className="input-container">
                    <InputField name="Company Name" id="comp-name" type="text" />
                    <InputField name="Company ID" id="comp-id" type="text" />
                    <InputField name="Username" id="username" type="text" />
                    <InputField name="Email" id="email" type="email" />
                    <InputField name="Password" id="password" type="password" />
                    <InputField name="Confirm Password" id="confirm-password" type="password" />
                </div>
                <div className="btns">
                    <Buttons name="Sign Up" id="signup-btn" type="submit" cls="success" />
                    <Buttons name="Log In" id="login-btn" type="button" cls="success" alt="true" onClick={toLogin} />
                </div>
            </form>
        );
    };
    let Login = () => {
        function toSignup() {
            setPage("signup");
        }
        return (
            <form action="" method="get" className="form">
                <div className="input-container">
                    <InputField name="Company ID" id="comp-id" type="text" />
                    <InputField name="Password" id="password" type="password" />
                </div>
                <div className="btns">
                    <Buttons name="Log In" id="login-btn" type="submit" cls="success" />
                    <Buttons name="Sign Up" id="signup-btn" type="submit" cls="success" alt onClick={toSignup} />
                </div>
            </form>
        );
    };
    let form = (formType) => (
        <div className="outer-wrapper">
            <div className="form-wrapper">
                <label className="form-title">{pageLabel}</label>
                {formType}
            </div>
        </div>
    );
    if (page === "signup") {
        const signup = Signup();
        return form(signup);
    } else if (page === "login") {
        const login = Login();
        return form(login);
    }
}

export default Form;
1 Answers

The reason why after you click on Login button and get Login page and the page immediately re-renders and you get the Signup page is conditional render in your example. Right after clicking on Login button your state still previous and for this reason after click you get the main (Signup) page.

Suggest to change the structure render into smth like this:

...
return (
  <div className="outer-wrapper">
    <div className="form-wrapper">
      <label className="form-title">{pageLabel}</label>
      {page === "signup" && Signup()}
      {page === "login" && Login()}
    </div>
  </div>
);

Here is the example in an action - https://codesandbox.io/s/smoosh-water-6jj18?file=/src/App.js

Related