7
\$\begingroup\$

I have been building an app in React Native and have decided that since my Login and Signup components share much of the same code, it would be best to create an AuthState component and pass login={true} or login={false} as props to trigger renderLogin() or renderSignup() in AuthState. However, in doing so, I think I have written extraneous code. I tried to minimize this by writing a renderEmailAndPasswordForms() function that gets included in renderLogin() and renderSignup() but I am sure there's better ways to clean all of this up. Can someone point me in the right direction? Here is my code:

login.js

import React, { Component } from 'react';
// Components
import AuthShared from '../auth_shared';
export default class Login extends Component {
 render() {
 return (
 <AuthShared login={true}/>
 );
 }
}

signup.js

import React, { Component } from 'react';
// Components
import AuthShared from '../auth_shared';
export default class SignUp extends Component {
 render() {
 return (
 <AuthShared login={false}/>
 );
 }
}

auth_shared.js

import React, { Component } from 'react';
import {
 AlertIOS,
 Dimensions,
 Image,
 ScrollView,
 StyleSheet,
 Text,
 TextInput,
 TouchableOpacity,
 View
} from 'react-native';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import { Actions } from 'react-native-router-flux';
import firebaseApp from 'TextbookSwap/firebase_setup';
import styles from 'TextbookSwap/app_styles';
// Components
import HeaderImage from './header_image';
// For Firebase Auth
const auth = firebaseApp.auth();
export default class Login extends Component {
 constructor(props) {
 super(props);
 this.state = {
 firstName: '',
 lastName: '',
 email: '',
 password: '',
 passwordConfirmation: ''
 }
 }
 componentDidMount() {
 let user = auth.currentUser;
 if (user) {
 console.log(msg)
 Actions.home
 } else {
 return;
 }
 }
 render() {
 return (
 <View style={styles.mainContainer}>
 <KeyboardAwareScrollView 
 style={styles.scrollView}
 keyboardShouldPersistTaps={false}
 automaticallyAdjustContentInsets={true}
 alwaysBonceVertical={false}
 >
 <View style={styles.formInputContainer}>
 <HeaderImage />
 {this.props.login ? this.renderLogin() : this.renderSignup()}
 </View>
 {this.props.login ? this.renderFooter() : null}
 </KeyboardAwareScrollView>
 </View>
 );
 }
 renderLogin() {
 return (
 <View>
 {this.renderEmailAndPasswordForms()}
 <View style={styles.authButtonContainer}>
 <TouchableOpacity
 style={styles.authButton}
 onPress={this._logInUser.bind(this)}
 >
 <Text style={styles.actionText}>Log me in!</Text>
 </TouchableOpacity>
 </View>
 </View>
 );
 }
 renderSignup() {
 return (
 <View>
 <View style={[styles.formInputWrapper, styles.formInputInlineWrapper]}>
 <View style={{borderColor: '#50514F', borderLeftWidth: 0, borderRightWidth: 0.5, borderTopWidth: 0, borderBottomWidth: 0}}>
 <TextInput
 style={[styles.formInput, styles.formInputInline]}
 autoFocus={true}
 autoCapitalize="none"
 autoCorrect={false}
 placeholder="First Name"
 onChangeText={(firstName) => this.setState({firstName})}
 />
 </View>
 <TextInput
 style={[styles.formInput, styles.formInputInline]}
 autoFocus={true}
 autoCapitalize="none"
 autoCorrect={false}
 placeholder="Last Name"
 onChangeText={(lastName) => this.setState({lastName})}
 />
 </View>
 {this.renderEmailAndPasswordForms()}
 <View style={styles.formInputWrapper}>
 <TextInput
 style={styles.formInput}
 secureTextEntry={true}
 autoCapitalize="none"
 autoCorrect={false}
 placeholder="Password Confirmation"
 onChangeText={(passwordConfirmation) => this.setState({passwordConfirmation})}
 />
 </View>
 <View style={styles.authButtonContainer}>
 <TouchableOpacity
 style={styles.authButton}
 onPress={this._signUpUser.bind(this)}
 >
 <Text style={styles.actionText}>Sign me up!</Text>
 </TouchableOpacity>
 </View>
 </View>
 );
 }
 renderEmailAndPasswordForms() {
 return (
 <View>
 <View style={styles.formInputWrapper}>
 <TextInput
 style={styles.formInput}
 autoFocus={true}
 autoCapitalize="none"
 autoCorrect={false}
 placeholder="Email"
 onChangeText={(email) => this.setState({email})}
 />
 </View>
 <View style={styles.formInputWrapper}>
 <TextInput
 style={styles.formInput}
 secureTextEntry={true}
 autoCapitalize="none"
 autoCorrect={false}
 placeholder="Password"
 onChangeText={(password) => this.setState({password})}
 />
 </View>
 </View>
 );
 }
 renderFooter() {
 return (
 <View style={styles.footer}>
 <TouchableOpacity
 style={styles.footerButton}
 onPress={Actions.signup}
 >
 <Text style={styles.actionText}>No account? Create one!</Text> 
 </TouchableOpacity>
 </View>
 );
 }
 _logInUser() {
 let email = this.state.email;
 let password = this.state.password;
 auth.signInWithEmailAndPassword(email, password)
 .then(Actions.home)
 .catch((error) => {
 switch(error.code) {
 case "auth/wrong-password":
 AlertIOS.alert('Uh oh!', 'Invalid password! Please try again.');
 break;
 case "auth/invalid-email":
 AlertIOS.alert('Uh oh!', 'Invalid email! Please try again.'); 
 break;
 case "auth/user-not-found":
 AlertIOS.alert('Uh oh!', 'Please check your credentials and try again');
 break;
 }
 });
 }
 _signUpUser() {
 }
}
Simon Forsberg
59.7k9 gold badges157 silver badges311 bronze badges
asked Aug 3, 2016 at 18:27
\$\endgroup\$
3
  • \$\begingroup\$ What version of React are you using? \$\endgroup\$ Commented Aug 18, 2016 at 6:50
  • \$\begingroup\$ I don't think that combining SignUp and Login is a good idea in your case. The occurrence of both renderSignup and renderLoginresp. _logInUser and signUpUser hint to me that SignUp and Login are more different than similar. Furthermore, the use of the flag login smells like they should be split up. Therefore, I would move this logic back into the respective components. It might make sense to create a custom input component for the email and password fields, though, and use that. \$\endgroup\$ Commented Nov 21, 2016 at 4:13
  • \$\begingroup\$ I'd say this is entirely too much code in a single file. Separate out your login and signup and compare them side by side. If you have duplicated code in them, make a separate component that has the parts you need, then import that in both of them. \$\endgroup\$ Commented Jan 13, 2017 at 8:51

1 Answer 1

3
\$\begingroup\$

React is component based, so i think you should not combine it, you should split component for the footer, component for the header, component for the container and if you want to render inside the container you can use

Page.jsx

...
render() {
 return (
 <MainContainer>
 <Header/>
 {React.Children.only(this.props.children)}
 <Footer/>
 </MainContainer>
 );
 }
...

then you can use it like this

Login.jsx

import React, { Component } from 'react';
import LoginForm from '../LoginForm.jsx';
import Page from '../Page.jsx';
class Login extends Component {
 render() {
 return (
 <Page children={LoginForm}/>
 );
 }
}
export default Login;
answered Mar 18, 2017 at 12:42
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.