简体   繁体   中英

How to avoid duplication of components using React-Navigation?

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)

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>

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM