I want to conditionally render jsx in my AuthForm component and i'm having trouble specifically with the 'isPasscodeEntry' scenario.
My AuthForm component conditionally renders an input form based on the state of isLogin, isForgot, and isPasscodeEntry.
AuthForm is imported and rendered in AuthContent.
AuthContent is conditionally rendered in my LoginScreen component based on the state of based on the state of isLogin, isForgot, and isPasscodeEntry.
isLogin = Login / !isLogin = Sign up / isForgot = enter email > get passcode email / isPasscodeEntry = enter passcode
In LoginScreen i'm console logging the state of isPasscodeEntry and see it change to true when I press 'reset password' but the isPasscodeEntry jsx is not being rendered.
To summarize:
When isForgot is true (Password Reset Flow):
If isForgot is true (Password Reset flow), the isPasscodeEntry JSX will be conditionally rendered when the "Reset Password" button is pressed.
When the "Reset Password" button is pressed (isForgot is true), it will call setIsPasscodeEntry().
When isForgot is false (Login or Signup Flow):
If isForgot is false (Login or Signup flow), the isPasscodeEntry JSX will not be conditionally rendered, and the "Reset Password" button will not be shown. Instead, it will show the "Log In" or "Sign Up" button depending on whether isLogin is true or false.
Here is the jsx for LoginScreen:
function AuthContent({ isLogin, isForgot, isPasscodeEntry, setIsPasscodeEntry, onSignUp, onLogin, onForgot }) {...
return (
<SafeAreaView style={styles.container}>
{!isForgot ? (
<>
{isPasscodeEntry ? (
<>
<AuthContent
isLogin={isLogin}
isPasscodeEntry={isPasscodeEntry}
setIsPasscodeEntry={() => setPasscodeEntry}
// onSignUp={signUpHandler}
// onLogin={loginHandler}
/>
<View style={styles.forgot}>
<Button onPress={() => { setIsForgot(true), setIsLogin(false) }}>
Forgot Password
</Button>
</View>
<View style={styles.buttons}>
<Button onPress={() => setIsLogin(!isLogin)}>
{isLogin ? 'Register Here' : 'Login Here'}
</Button>
{isLoading && <LoadingOverlay />}
</View>
</>
) : (
<>
<AuthContent
isLogin={isLogin}
isPasscodeEntry={isPasscodeEntry}
setIsPasscodeEntry={() => setPasscodeEntry}
onSignUp={signUpHandler}
onLogin={loginHandler}
/>
<View style={styles.forgot}>
<Button onPress={() => { setIsForgot(true), setIsLogin(false) }}>
Forgot Password
</Button>
</View>
<View style={styles.buttons}>
<Button onPress={() => setIsLogin(!isLogin)}>
{isLogin ? 'Register Here' : 'Login Here'}
</Button>
{isLoading && <LoadingOverlay />}
</View>
</>
)}
</>
) : (
<>
<AuthContent
isLogin={false}
isForgot={isForgot}
onLogin={forgotHandler}
onForgot={forgotHandler}
isPasscodeEntry={isPasscodeEntry}
setIsPasscodeEntry={() => setPasscodeEntry()}
/>
<View style={styles.buttons}>
<Button onPress={() => setIsForgot(false)}>
Go Back
</Button>
{isLoading && <LoadingOverlay />}
</View>
</>
)}
</SafeAreaView>
);
}
export default LoginScreen;
And here is the jsx for AuthForm:
function AuthForm({ isLogin, isForgot, isPasscodeEntry, setIsPasscodeEntry, onSubmit, credentialsInvalid }) {...
return (
<KeyboardAvoidingView
style={{ flex: 1 }}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 100}
>
<View style={styles.form}>
<View>
{isForgot ? (
<Input
label="Email Address"
onUpdateValue={updateInputValueHandler.bind(this, 'email')}
value={enteredEmail}
textInputConfig={{
onChangeText: (text) => updateInputValueHandler('email', text),
value: enteredEmail,
}}
keyboardType="email-address"
isInvalid={emailIsInvalid}
/>
) : (
<>
<Input
label="Email Address"
onUpdateValue={updateInputValueHandler.bind(this, 'email')}
value={enteredEmail}
textInputConfig={{
onChangeText: (text) => updateInputValueHandler('email', text),
value: enteredEmail,
}}
keyboardType="email-address"
isInvalid={emailIsInvalid}
/>
{!isLogin && !isForgot && (
<Input
label="Confirm Email Address"
onUpdateValue={updateInputValueHandler.bind(this, 'confirmEmail')}
value={enteredConfirmEmail}
keyboardType="email-address"
isInvalid={emailsDontMatch}
/>
)}
<Input
label="Password"
onUpdateValue={updateInputValueHandler.bind(this, 'password')}
value={enteredPassword}
textInputConfig={{
onChangeText: (text) => updateInputValueHandler('password', text),
value: enteredPassword,
secureTextEntry: true,
}}
isInvalid={passwordIsInvalid}
/>
{!isLogin && !isForgot && (
<Input
label="Confirm Password"
onUpdateValue={updateInputValueHandler.bind(this, 'confirmPassword')}
value={enteredConfirmPassword}
textInputConfig={{
onChangeText: (text) => updateInputValueHandler('confirmPassword', text),
value: enteredConfirmPassword,
secureTextEntry: true,
}}
isInvalid={passwordsDontMatch}
/>
)}
{isPasscodeEntry && (
// Add your passcode entry component here
<>
<Input
label="Enter one-time passcode"
onUpdateValue={updateInputValueHandler.bind(this, 'passcode')}
value={enteredCode}
textInputConfig={{
onChangeText: (text) => updateInputValueHandler('passcode', text),
value: enteredCode,
secureTextEntry: true,
}}
isInvalid={passwordIsInvalid}
/>
</>
)}
</>
)}
</View>
<View style={styles.buttons}>
{isForgot ? (
// Call setIsPasscodeEntry(true) when isForgot is true
<Button onPress={() => { submitHandler(); setIsPasscodeEntry(); }}>
Reset Password
</Button>
) : (
// Call submitHandler when isForgot is false
<Button onPress={submitHandler}>
{isLogin ? 'Log In' : 'Sign Up'}
</Button>
)}
</View>
</View>
</KeyboardAvoidingView>
);
}
export default AuthForm;
Expectation: "If isForgot is true (Password Reset flow), the isPasscodeEntry JSX will be conditionally rendered when the "Reset Password" button is pressed."
The main problem: Your passcode entry component only renders when isForgot is set to false AND isPasscodeEntry is set to true according to your conditions. This is not the logic you planned in your question.
Solution: Move the passcode entry component with its conditional rendering to a branch where isForgot is set to true
I can't show you the exact solution because you have complicated nesting conditions.
I suggest:
improving the variable naming for better readability
go over all the conditions to make sure all the conditional rendering is working as intended.
wrap your view components with a meaningful function so you can easily tell what you are rendering under each condition.
avoid nesting ternary operators or if else conditions (creates confusing branching logic), instead use combined logical operators
Ternary Logic Example:
isForgot ?
isPasscodeEntry ?
renderPasscodeEntry() :
renderEmailInput()
: renderSomethingElse()
Combined Logical Operators Alternative:
<>
{isForgot && hasPasscodeEntry && renderPasscodeEntry()}
{isForgot && !hasPasscodeEntry && renderEmailInput()}
{!isForgot && renderSomethingElse()}
</>