Reputation: 719
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
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