-1
\$\begingroup\$

I am using react native 0.68.5

When I call the renderReusableTextInput method in Main.js I am sending all the styles including the needed and unneeded styles. This can lead performance penalty.

So, is there a way that I can send the only needed styles to the renderReusableTextInput method.

I also want to know is the code reusing approach of mine correct or there are better ways.

I have the following code-logic structure:

(i) A Reusable component; It has only structure no style or state

(ii) A file that includes reusable methods

(iii) The Main component, which contains components, styles and states

ReusableTextInput.js


// import ...

const ReusableTextInput = forwardRef(({
  inputName,
  inputLabel,
  inputValue,
  secureTextEntry,
  textInputContainerWarperStyle,
  textInputContainerStyle,
  textInputLabelStyle,
  textInputStyle,
  textInputHelperStyle,
  textInputErrorStyle,
  helperText,
  inputError,
  onFocus,
  onChangeText,
  onBlur,
}, inputRef) => {

  return (
    <View style={[textInputContainerWarperStyle]}>
      <View style={[textInputContainerStyle]}>
    <Text style={[textInputLabelStyle]}>
      {inputLabel}
    </Text>
    <TextInput
      ref={(elm) => inputRef[inputName] = elm}
      style={[textInputStyle]}
      value={inputValue}
      secureTextEntry={secureTextEntry}
      onChangeText={onChangeText}
      onFocus={onFocus}
      onBlur={onBlur}
    />
      </View>
      {
        ((inputRef[inputName]) && (inputRef[inputName].isFocused()) && (!inputValue))
          ?
          <Text style={[textInputHelperStyle]}>
            {helperText}
          </Text>
          :
          null
      }
      {
        ((inputError) && (inputValue))
          ?
          <Text style={[textInputErrorStyle]}>
            {inputError}
          </Text>
          :
          null
      }
    </View>
  );
});

export default memo(ReusableTextInput);

reusableMethods.js


// import ...

const handleFocus = (state, setState, styles) => {
  // here styles contains all style key which I do not want
  // I only want the required styles

  const stateData = { ...state };

  stateData.styleNames.textInputContainer = {
    ...styles.textInputContainer,
    ...styles[`${stateData.name}ExtraTextInputContainer`],
    ...styles.textInputContainerFocus,
  };
  stateData.styleNames.textInputLabel = {
    ...styles.textInputLabel,
    ...styles[`${stateData.name}ExtraTextInputLabel`],
    ...styles.textInputLabelFocus,
  };
  stateData.styleNames.textInput = {
    ...styles.textInput,
    ...styles[`${stateData.name}ExtraTextInput`],
    ...styles.textInputFocus,
  };

  // other logics...

  setState(stateData);
};

const handleChangeText = (state, setState, text) => {
  const stateData = { ...state };

  // individual validation
  const schemaData = Joi.object().keys(stateData.validationObj); // I used Joi for validation
  const inputData = { [stateData.name]: text };

  const options = { abortEarly: false, errors: { label: false } };

  const result = schemaData.validate(inputData, options);
  // -----

  stateData.error = (result.error) ? result.error.details[0].message : '';

  stateData.value = text;

  // other logics...

  setState(stateData);
};

const handleBlur = (state, setState, styles) => {
  // here styles contains all style key which I do not want
  // I only want the required styles

  const stateData = { ...state };

  if (stateData.value) {
    stateData.styleNames.textInputContainer = {
      ...styles.textInputContainer,
      ...styles[`${stateData.name}ExtraTextInputContainer`],
      ...styles.textInputContainerFocus,
      ...styles[`${stateData.name}ExtraTextInputContainerFocus`],
      ...styles.textInputContainerBlurText,
      ...styles[`${stateData.name}ExtraTextInputContainerBlurText`],
    };
    stateData.styleNames.textInputLabel = {
      ...styles.textInputLabel,
      ...styles[`${stateData.name}ExtraTextInputLabel`],
      ...styles.textInputLabelFocus,
      ...styles[`${stateData.name}ExtraTextInputLabelFocus`],
      ...styles.textInputLabelBlurText,
      ...styles[`${stateData.name}ExtraTextInputLabelBlurText`],
    };
    stateData.styleNames.textInput = {
      ...styles.textInput,
      ...styles[`${stateData.name}ExtraTextInput`],
      ...styles.textInputFocus,
      ...styles[`${stateData.name}ExtraTextInputFocus`],
      ...styles.textInputBlurText,
      ...styles[`${stateData.name}ExtraTextInputBlurText`],
    };
  }
  else {
    stateData.styleNames.textInputContainer = { ...styles.textInputContainer, ...styles[`${stateData.name}ExtraTextInputContainer`] };
    stateData.styleNames.textInputLabel = { ...styles.textInputLabel, ...styles[`${stateData.name}ExtraTextInputLabel`] };
    stateData.styleNames.textInput = { ...styles.textInput, ...styles[`${stateData.name}ExtraTextInput`] };
  }

  // other logics...

  setState(stateData);
};


// other methods...


export const renderReusableTextInput = (
  state,
  setState,
  inputRef,
  styles, // contains all the styles from Main component
) => {

  return (
    <ReusableTextInput
      inputName={state.name}
      inputLabel={state.label}
      inputValue={state.value}
      inputRef={inputRef}
      secureTextEntry={state.secureTextEntry}
      textInputContainerWarperStyle={{...styles.textInputContainerWarper, ...styles[`${state.name}ExtraTextInputContainerWarper`]}}
      textInputContainerStyle={state.styleNames.textInputContainer}
      textInputLabelStyle={state.styleNames.textInputLabel}
      textInputStyle={state.styleNames.textInput}
      textInputHelperStyle={{...styles.textInputHelper, ...styles[`${state.name}ExtraTextInputHelper`]}}
      textInputErrorStyle={{...styles.textInputError, ...styles[`${state.name}ExtraTextInputError`]}}
      helperText={state.helperText}
      inputError={state.error}
      onFocus={() => handleFocus(state, setState, styles)}
      onChangeText={(text) => handleChangeText(state, setState, text)}
      onBlur={() => handleBlur(state, setState, styles)}
    />
  );
};

Main.js


// import Joi from 'joi';
// import { joiPasswordExtendCore } from 'joi-password';

// import { renderReusableTextInput } from ''; ...


const schema =
{
  email: Joi.string().strict()
    .case("lower")
    .min(5)
    .max(30)
    .email({ minDomainSegments: 2, tlds: { allow: ["com", "net", "org"] } })
    .required(),

  countryCode: // Joi.string()...,

  phoneNumber: // Joi.string()...,

  password: // Joi.string()...,
  // ...
};

const Main = () => {

  const { width: windowWidth, height: windowHeight, scale, fontScale } = useWindowDimensions();

  const minimumWidth = (windowWidth <= windowHeight) ? windowWidth : windowHeight;

  const styles = useMemo(() => currentStyles(minimumWidth), [minimumWidth]);

  const [email, setEmail] = useState({
    name: 'email', // unchangeable
    label: 'Email', // unchangeable
    value: '',
    error: '',
    validationObj: { email: schema.email },  // unchangeable
    trailingIcons: [require('../../file/image/clear_trailing_icon.png')], // unchangeable
    helperText: 'only .com, .net and .org allowed', // unchangeable
    styleNames: {
      textInputContainer: { ...styles.textInputContainer, ...styles.emailExtraTextInputContainer },
      textInputLabel: { ...styles.textInputLabel, ...styles.emailExtraTextInputLabel },
      textInput: { ...styles.textInput, ...styles.emailExtraTextInput },
    },
  });

  const [phoneNumber, setPhoneNumber] = useState({
  });

  const [countryCode, setCountryCode] = useState({
  });

  const [password, setPassword] = useState({
  });

  // ...
 
  const references = useRef({});


  return (
    <View style={[styles.mainContainer]}>
      {
        useMemo(() => renderReusableTextInput(email, setEmail, references.current, styles), [email, minimumWidth])
      }
    </View>
  );
}

export default memo(Main);



const styles__575 = StyleSheet.create({
  // 320 to 575
  mainContainer: {
  },
  textInputContainerWarper: {
  },
  emailExtraTextInputContainerWarper: {
  },
  countryCodeExtraTextInputContainerWarper: {
  },
  phoneNumberExtraTextInputContainerWarper: {
  },
  passwordExtraTextInputContainerWarper: {
  },
  textInputContainer: {
  },
  emailExtraTextInputContainer: {
  },
  textInputContainerFocus: {
  },
  textInputContainerBlurText: {
  },
  textInputLabel: {
  },
  emailExtraTextInputLabel: {
  },
  textInputLabelFocus: {
  },
  textInputLabelBlurText: {
  },
  textInput: {
  },
  emailExtraTextInput: {
  },
  textInputFocus: {
  },
  textInputBlurText: {
  },
  textInputHelper: {
  },
  emailExtraTextInputHelper: {
  },
  textInputError: {
  },
  emailExtraTextInputError: {
  },

  // other styles...
});

const styles_576_767 = StyleSheet.create({
  // 576 to 767 
});

const styles_768_ = StyleSheet.create({
  // 768; goes to 1024;
});

const currentStyles = (width, stylesInitial = { ...styles__575 }, styles576 = { ...styles_576_767 }, styles768 = { ...styles_768_ }) => {
  let styles = {};

  if (width < 576) {
  }
  else if ((width >= 576) && (width < 768)) {
  }
  else if (width >= 768) {
  }

  return styles;
};

\$\endgroup\$
4
  • \$\begingroup\$ To help reviewers give you better answers, we need to know what the code is intended to achieve. Please add sufficient context to your question to describe the purpose of the code. We want to know why much more than how. The more you tell us about what your code is for, the easier it will be for reviewers to help you. Also, edit the title to simply summarise the task, rather than your concerns about the code. \$\endgroup\$ Commented Dec 22, 2022 at 9:46
  • \$\begingroup\$ @TobySpeight, I will try. \$\endgroup\$ Commented Dec 22, 2022 at 13:17
  • \$\begingroup\$ This code seems incomplete. For example, currentStyles has if statements for various width ranges, all of which are empty. Please post complete code. \$\endgroup\$ Commented Dec 23, 2022 at 5:12
  • \$\begingroup\$ @AJNeufeld, for simplicity I wrote that way, nothing important inside those empty if blocks. If I wrote full code for every block, then it will be more complicated to understand. So, I intentionally wrote this post that way. Thanks. \$\endgroup\$ Commented Dec 23, 2022 at 11:49

1 Answer 1

1
\$\begingroup\$

First, I think you want global styles, so that you can access it from anywhere and you can also be able to execute needed code.

Make sure you use useMemo, useCallback in right manner, for better performance.

Move all Schema and Styles and Methods of your screens inside the reusableMethods.js file (at most case). It will act like the controller of all screens of your app, also make a demo method which return a tiny component and this method execute another method, so that you can get styles for different dimentions(see below code)

Store only changeable and needed properties in state variables.

Is code reusing approach of yours, correct? I can't say about that. I will say that it depends on developer choice.

you can try like below:

reusableMethods.js


// import all schema, style variants, utility methods and other

let screenStyles = {};

const executeDimensionBasedMethods = (width, screenName) => {
  if (screenName === 'a_screen_name') screenStyles[screenName] = currentStyles(width, otherParameter);
  // else if()
  // ...
};

export const renderDimension = (width, screenName) => {
  executeDimensionBasedMethods(width, screenName);
  return (
    <Text style={{ width: 0, height: 0 }}></Text>
  );
};

// all method definitions and logics

export const renderReusableTextInput = (
  state,
  setState,
  inputRef,
  screenName
) => {
  return (
    <ReusableTextInput 
      inputName={state.name}
      inputLabel={state.label}
      inputValue={state.value}
      inputRef={inputRef}
      secureTextEntry={state.secureTextEntry}
      textInputContainerWarperStyle={{ ...screenStyles[screenName].textInputContainerWarper, ...screenStyles[screenName][`${state.name}ExtraTextInputContainerWarper`] }}
      textInputContainerStyle={state.styleNames.textInputContainer || { ...screenStyles[screenName].textInputContainer }
      textInputLabelStyle={state.styleNames.textInputLabel || { ...screenStyles[screenName].textInputLabel }
      textInputStyle={state.styleNames.textInput || { ...screenStyles[screenName].textInput }
      textInputHelperStyle={{ ...screenStyles[screenName].textInputHelper, ...screenStyles[screenName][`${state.name}ExtraTextInputHelper`] }}
      textInputErrorStyle={{ ...screenStyles[screenName].textInputError, ...screenStyles[screenName][`${state.name}ExtraTextInputError`] }}
      helperText={state.helperText}
      inputError={state.error}
      onFocus={() => handleFocus(state, setState, screenStyles[screenName])}
      onChangeText={(text) => handleChangeText(state, setState, text)}
      onBlur={() => handleBlur(state, setState, screenStyles[screenName])}
    />
  );
};

Main.js


const Main = () => {
  const { width: windowWidth, height: windowHeight} = useWindowDimensions();

  const minimumWidth = (windowWidth <= windowHeight) ? windowWidth : windowHeight;

  const [email, setEmail] = useState({
    name: 'email', // unchangeable
    label: 'Email', // unchangeable
    value: '',
    error: '',
    trailingIcons: [require('../../file/image/clear_trailing_icon.png')], // unchangeable
    helperText: 'only .com, .net and .org allowed', // unchangeable
    styleNames: {
      textInputContainer: undefined,
      textInputLabel: undefined,
      textInput: undefined,
    },
  });

  // other states 

  const references = useRef({});


  return (
    <>
      {
        useMemo(() => renderDimension(minimumWidth), [minimumWidth])
      }
      <View style={{ // inline style}}>
        {
          useMemo(() => renderReusableTextInput(email, setEmail, references.current, 'nameofscreen'), [email, minimumWidth])
        }
      </View>
  </>
}

export default memo(Main);
```
\$\endgroup\$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.