Alex Gogl
Alex Gogl

Reputation: 719

How to update multiple elements conditionally at once

I'm new to React and trying to figure out what the best way is to update multiple elements at once without having to repeat code all the time.

I got a login page, that has several states: "LOGIN", "SIGNUP" or "FORGOT_PASSWORD". Depending on the current mode, I am already able to change the used form with renderForm(). However, I also need to update some other text in the root component but don't know what the most efficient way is to do so. Of course I could repeat renderForm() for every text snippet I have to change but this seems not very DRY.

This is my (very simplified) code:

function Login() {
    const [mode, setMode] = useState("LOGIN");

    function renderForm(): ReactElement {
    return (
      <>
        {
          {
            LOGIN: <EmailPasswordForm setMode={setMode} type="login"/>,
            SIGNUP: <EmailPasswordForm setMode={setMode} type="signup"/>,
            FORGOT_PASSWORD: <ForgotPassword setMode={setMode} auth={auth} />,
          }[mode]
        }
      </>
    );
  }


    return (
    <div>
        <h2>
            Sign in to your account //title that needs to change depending on the mode
        </h2>

        {renderForm()}

        <a href="/privacy">Privacy Info</a>
        <p>
            Some text that also needs to change depending on if the mode is "LOGIN", "SIGNUP", or "FORGOT_PASSWORD"
        </p>
        <OAuthSignUpComponents/>
        <p>
            Some  more text that needs to change depending on if the mode is "LOGIN", "SIGNUP", or "FORGOT_PASSWORD"
        </p>
    </div>
    )
}

function EmailPasswordForm({setMode, type}) {

    const handleSubmit = () => {
        type == "login" ? loginLogic() : signupLogic();
    }

    return(
        <form>
            <input type="email/>
            <input type="password"/>
            <button type="button" onClick={handleSubmit}>
            { type == "login" ? <button onClick={setMode("SIGNUP")>Sign up instead</button> : <button onClick={setMode("LOGIN")>Sign in instead</button> }
        </form>
    )
}

function ForgotPasswordForm({setMode}) {
    return(
        <form>
            <input type="email/>
            <button type="button">
        </form>
    )
}

One thing I tried is to use a switch in renderForm() like this:

const [title, setTitle] = useState("Sign in to your account");

function renderForm(){
    switch(mode) {
        case "LOGIN":
            setTitle("Sign in to your account")
            return <EmailPasswordForm setMode={setMode} type="login"/>;
        case "SIGNUP":
            setTitle("Sign in to your account")
            return <EmailPasswordForm setMode={setMode} type="signup"/>;
        case "FORGOT_PASSWORD":
            setTitle("Reset Password")
            return <ForgotPasswordForm setMode={setMode}/>
    }
}

But that doesn't work either as it results in a too many rerenders error.

Upvotes: 0

Views: 44

Answers (1)

Zachary Haber
Zachary Haber

Reputation: 11037

You should have the setTitle in an effect in order for there to be no more rendering issues as you shouldn't have side-effets in your main render function, only in callbacks and effects. You can definitely keep the render form as an object instead of a switch case, as well. Either way would work.

    useEffect(() => {
    switch(mode) {
        case "LOGIN":
            setTitle("Sign in to your account")
        case "SIGNUP":
            setTitle("Sign in to your account")
        case "FORGOT_PASSWORD":
            setTitle("Reset Password")
    }
    return () => {
        // You can then set the original title of the application to clean up once they've logged in.
        // setTitle('Original Title')
    }
}, [mode])

function renderForm(){
    switch(mode) {
        case "LOGIN":
            return <EmailPasswordForm setMode={setMode} type="login"/>;
        case "SIGNUP":
            return <EmailPasswordForm setMode={setMode} type="signup"/>;
        case "FORGOT_PASSWORD":
            return <ForgotPasswordForm setMode={setMode}/>
    }
}

You don't even have to have your switch case or your object in an internal function either.

  const formMap={
    LOGIN: <EmailPasswordForm setMode={setMode} type="login"/>,
    SIGNUP: <EmailPasswordForm setMode={setMode} type="signup"/>,
    FORGOT_PASSWORD: <ForgotPassword setMode={setMode} auth={auth} />,
  };

  return(<div>
    <h2>
        Sign in to your account //title that needs to change depending on the mode
    </h2>

    {formMap[mode]}

    <a href="/privacy">Privacy Info</a>
    <p>
        Some text that also needs to change depending on if the mode is "LOGIN", "SIGNUP", or "FORGOT_PASSWORD"
    </p>
    <OAuthSignUpComponents/>
    <p>
        Some  more text that needs to change depending on if the mode is "LOGIN", "SIGNUP", or "FORGOT_PASSWORD"
    </p>
</div>)

Upvotes: 1

Related Questions