Reputation: 93
I had 5 screens with the same styles and layout, but some text and button logic difference.
I tried to keep everything in one component, passing there the name of the component I need to mimic, but it grew up in nested if/else all around, what made the code very intricate.
What is lesser of these two evils and what should I do: duplicate the components in favor of simplicity or keep them in one place and lose the readability?
Here's the "all in one" component
const Pin = props => {
const {
navigation: { navigate, getParam },
loading,
savePin,
signIn,
toggleModal,
} = props
const [pin, setPin] = useState('')
const isInSignInStack = getParam('isInSignInStack') ? 'isInSignInStack' : false
const isConfirmPinSignUp = getParam('isConfirmPinSignUp') ? 'isConfirmPinSignUp' : false
const isChangePin = getParam('isChangePin') ? 'isChangePin' : false
const isEnterNewPin = getParam('isEnterNewPin') ? 'isEnterNewPin' : false
const isConfirmNewPin = getParam('isConfirmNewPin') ? 'isConfirmNewPin' : false
const newPin = getParam('newPin')
const handleSavePin = () => savePin(pin).then(() => navigate('ConfirmPinSignUp'))
const navigateHome = () => navigate('Home')
const handleAuthenticate = () =>
compose(
then(navigateHome),
then(signIn),
savePin
)(pin)
const validatePin = () =>
isConfirmNewPin
? equals(newPin, pin)
? savePin(pin).then(() => navigate('SuccessPinChange'))
: toggleModal('pin isn't match')
: getPin().then(({ password }) =>
equals(password, pin)
? navigate(isChangePin ? 'ConfirmNewPin' : 'Success', { ...(isChangePin ? { newPin: pin } : {}) })
: toggleModal('pin isn't match')
)
const textObj = {
isInSignInStack: 'Enter your pin',
isConfirmPinSignUp: 'Enter your pin once again',
isChangePin: 'Enter your old pin',
isEnterNewPin: 'Enter the new pin',
isConfirmNewPin: 'Enter the new pin once again',
}
return (
<Container style={styles.container}>
<Content scrollEnabled={false} contentContainerStyle={styles.content}>
<Text style={styles.headerText}>
{pathOr(
'Come up with the new pin',
isInSignInStack || isConfirmPinSignUp || isChangePin || isEnterNewPin || isConfirmNewPin,
textObj
)}
</Text>
<View style={styles.inputView}>
<CodeInput />
</View>
{isConfirmPinSignUp || (
<View style={styles.aknowledgementView}>
{isInSignInStack
? <Text style={styles.text} onPress={handleForgotPassword}>
FORGOT PIN
</Text>
: isEnterNewPin && (
<>
<Text style={styles.greenText}>Attention! Don't use your old pin</Text>
<Text style={styles.greenText}>codes or passwords, come up with the new one</Text>
</>
)}
</View>
)}
<Button
style={isEmpty(pin) ? styles.btnDisabled : styles.btn}
onPress={() =>
isInSignInStack
? handleAuthenticate
: anyTrue(isConfirmPinSignUp, isChangePin) ? validatePin : handleSavePin
}
disabled={anyTrue(isEmpty(pin), loading)}
>
{loading ? <Spinner color="black" /> : <Text style={styles.btnText}>Next</Text>}
</Button>
</Content>
</Container>
)
}
Pin.navigationOptions = ({ navigation: { getParam } }) => {
const isInSignInStack = getParam('isInSignInStack')
const isChangePin = getParam('isChangePin')
const isEnterNewPin = getParam('isEnterNewPin')
return {
title: isInSignInStack ? 'SignIn' : anyTrue(isChangePin, isEnterNewPin) ? 'Change PIN' : 'Register'
}
}
const styles = StyleSheet.create({
//
})
Pin.propTypes = {
navigation: PropTypes.shape({
navigate: PropTypes.func,
getParam: PropTypes.func,
}).isRequired,
loading: PropTypes.bool.isRequired,
savePin: PropTypes.func.isRequired,
toggleModal: PropTypes.func.isRequired,
signIn: PropTypes.func.isRequired,
}
const mapStateToProps = compose(
pick(['loading']),
path(['user'])
)
const mapDispatchToProps = {
savePin,
signIn,
toggleModal,
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Pin)
Upvotes: 1
Views: 1912
Reputation: 93
The answer is that you should not couple the components which belong to different use cases as they will change for a different reasons and at different times, even though they look identically now they will change later. Don't write the "super component" that cover all the use cases, because it will become a mess very quickly
Upvotes: 0
Reputation: 742
Make a generic button component that takes a click handler and text as a prop then simply pass the values as props:
for instance if you have some button component like:
export const Button = ({ children, handler }) =>
<button onPress={handler}>
{children}
</button>;
Then you could use it like
<Button handler={this.yourClickHandler} >{"Some Text"}</Button>
Upvotes: 3