React anti-patterns - Splitting the render method into functions

Viewed 484

Is splitting the render method of a component into functions considered an anti-pattern?

I mean, I now that we can pefectly split it into more components if the JSX grows too big...

But, what about the following example:

  /**
   * Renders the text inputs of the form.
   *
   * @returns {React.ReactElement} The text inputs.
   */
  const renderInputs = () => (
    <View style={styles.inputsContainer}>
      <UsernameInput
        ref={usernameInputRef}
        label={t(
          "authentication.signUp.accountInformation.usernameInputLabel"
        )}
        returnKeyType="next"
        onChange={handleOnTextInputChange}
        onSubmitEditing={() => emailInputRef.current.focus()}
        containerStyle={commonStyles.textInputContainer}
      />

      <TextInput
        ref={emailInputRef}
        label={t("authentication.signUp.accountInformation.emailInputLabel")}
        maxLength={MAX_EMAIL_LENGTH}
        textContentType="emailAddress"
        keyboardType="email-address"
        returnKeyType="next"
        icon={{
          name: "email",
          type: "material",
          color: colors.scorpion,
        }}
        onChange={handleOnTextInputChange}
        onSubmitEditing={() => passwordInputRef.current.focus()}
        containerStyle={commonStyles.textInputContainer}
      />

      <PasswordInput
        ref={passwordInputRef}
        label={t(
          "authentication.signUp.accountInformation.passwordInputLabel"
        )}
        textContentType="newPassword"
        returnKeyType="next"
        onChange={handleOnTextInputChange}
        onSubmitEditing={() => repeatPasswordInputRef.current.focus()}
        containerStyle={commonStyles.textInputContainer}
      />

      <PasswordInput
        ref={repeatPasswordInputRef}
        label={t(
          "authentication.signUp.accountInformation.repeatPasswordInputLabel"
        )}
        textContentType="oneTimeCode"
        returnKeyType="done"
        blurOnSubmit
        onChange={handleOnTextInputChange}
        containerStyle={commonStyles.textInputContainer}
      />
    </View>
  );

  /**
   * Renders a button for continuing to the next screen.
   *
   * @returns {React.ReactElement} The *'continue'* button. 
   */
  const renderContinueButton = () => (
    <Button
      disabled={isContinueDisabled}
      uppercase
      mode="contained"
      onPress={handleOnContinue}
      style={styles.button}
      labelStyle={globalStyles.buttonLabel}
    >
      {t("authentication.signUp.accountInformation.continueButton")}
    </Button>
  );

  return (
    <View style={globalStyles.flexHorizontallyCenteredContainer}>
      {renderInputs()}
      {renderContinueButton()}
    </View>
  );
}

Should I avoid splitting the code here? As you can see... I am using custom components for the most "atomic" parts... and two inner helper methods to render them with the corresponding layout tweaks.

Pattern or anti-pattern?

2 Answers

No, abstracting the UI into different JSX components is not an anti-pattern. It is the opposite. It is recommended.

Sometimes, it might might more sense to create new JSX components instead. Thus, instead of writing

<View style={globalStyles.flexHorizontallyCenteredContainer}>
      {renderInputs()}
      {renderContinueButton()}
</View>

You could directly create the JSX component as a child of the View as follows.

<View style={globalStyles.flexHorizontallyCenteredContainer}>
      <renderInputs />
      <renderContinueButton />
</View>

Since both of them are JSX components. In this case, it is generally expected that a JSX component starts with a capital letter and isn't named using a verb. Thus, I would recommend naming them as follows.

const Inputs = () => {
 return (...)
}
const ContinueButton = () => {
  return (...)
}

While it is fine to define components inside a another component and then create it inside the render function of the main component, it often is advised to create new files. Thus, Inputs and ContinueButton would be placed inside a new file, e.g. Inputs.js and ContinueButton.js. You can export them as usual and import them. This makes it possible to reuse components in different places.

I don't have the karma to comment - relatively new to posting here.

I want to address the final paragraph in David's answer: "While it is fine to define components inside a another component and then create it inside the render function of the main component, it often is advised to create new files." (emphasis mine)

It is not fine to define a component inside of another component. When you define a component inside of another component it get's repeatedly destroyed and recreated on each render. Mark Erikson, one of the maintainers of Redux, has an awesome article on React rendering and talks more about why this is an issue here.

If you want to define a new component define it outside of the component or, as David suggests, define it in a new file.

Related