Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Refactoring code for custom date component

I have created a date component (working GIF at the bottom).

There isn't a problem with the working of the code but rather the code I wrote seems messy and something hard for any other person to comprehend.

Note: Please look at the GIF below. Also, Ignore the styling

this is what I am doing. For the date Component in screen, I am creating refs and state like this

class OnBoarding extends PureComponent {
    constructor(props) {
        super(props)
        this.d1 = React.createRef()
        this.d2 = React.createRef()
        this.d3 = React.createRef()
        this.d4 = React.createRef()
        this.d5 = React.createRef()
        this.d6 = React.createRef()
        this.d7 = React.createRef()
        this.d8 = React.createRef()
    }
    state = {
        name: '',
        emailAddress: '',
        dob: '',
        male: null,
        female: null,
        keyboard: false,
        d1: null,
        d2: null,
        d3: null,
        d4: null,
        d5: null,
        d6: null,
        d7: null,
        d8: null
    }

dobHandler(number, flag) {
        const completeFlag = `d${flag}`
        this.setState({[completeFlag]: number})
        flag = flag + 1
        if (flag < 9 && number) {
            const nextFlag = `d${flag}`
            const textInputToFocus = this[nextFlag]
            textInputToFocus.current.focus()
        }
    }

And then rendering them like this

       <View style={styles.dob}>
                        <TextInput
                            ref={this.d1}
                            numberOfLines={1}
                            maxLength={1}
                            style={styles.textInputDob}
                            keyboardType="numeric"
                            placeholder="D"
                            onChangeText={number => this.dobHandler(number, 1)}
                        />
                        <TextInput
                            ref={this.d2}
                            numberOfLines={1}
                            maxLength={1}
                            style={styles.textInputDob}
                            keyboardType="numeric"
                            placeholder="D"
                            onChangeText={number => this.dobHandler(number, 2)}
                        />
                        <Text>/</Text>
                        <TextInput
                            ref={this.d3}
                            numberOfLines={1}
                            maxLength={1}
                            style={styles.textInputDob}
                            keyboardType="numeric"
                            placeholder="M"
                            onChangeText={number => this.dobHandler(number, 3)}
                        />
                        <TextInput
                            ref={this.d4}
                            numberOfLines={1}
                            maxLength={1}
                            style={styles.textInputDob}
                            keyboardType="numeric"
                            placeholder="M"
                            onChangeText={number => this.dobHandler(number, 4)}
                        />
                        <Text>/</Text>
                        <TextInput
                            ref={this.d5}
                            numberOfLines={1}
                            maxLength={1}
                            style={styles.textInputDob}
                            keyboardType="numeric"
                            placeholder="Y"
                            onChangeText={number => this.dobHandler(number, 5)}
                        />
                        <TextInput
                            ref={this.d6}
                            numberOfLines={1}
                            maxLength={1}
                            style={styles.textInputDob}
                            keyboardType="numeric"
                            placeholder="Y"
                            onChangeText={number => this.dobHandler(number, 6)}
                        />
                        <TextInput
                            ref={this.d7}
                            numberOfLines={1}
                            maxLength={1}
                            style={styles.textInputDob}
                            keyboardType="numeric"
                            placeholder="Y"
                            onChangeText={number => this.dobHandler(number, 7)}
                        />
                        <TextInput
                            ref={this.d8}
                            numberOfLines={1}
                            maxLength={1}
                            style={styles.textInputDob}
                            keyboardType="numeric"
                            placeholder="Y"
                            onChangeText={number => this.dobHandler(number, 8)}
                        />
                    </View>

The reason I have made so many ref is because the moment someone enters something in the current textInput, I want the focus to moved to the next one, which happens in dobHandler function.

Can someone help me in improving quality/optimizing and if this is the wrong way of doing it, then hint me on How to achieve this alternativekly

enter image description here

like image 410
anny123 Avatar asked Jun 21 '19 14:06

anny123


3 Answers

Among many ways, you can write this as,

const placeholders = [ 'D', 'D', 'M', 'M', 'Y', 'Y', 'Y', 'Y'];

class OnBoarding extends PureComponent {
  constructor(props) {
    super(props)
    this.refs = Array(8).fill(0).map(_ => React.createRef())
  }

  state = {
    name: '',
    emailAddress: '',
    male: null,
    female: null,
    keyboard: false,
    dob: Array(8).fill(null)
  }

  dobHandler(number, index) {
    const { dob } = this.state
    dob[index] = number;
    this.setState({ dob:  [ ...dob ]})
    const ref = this.refs[index + 1]
    if (number && ref && ref.current)
      ref.current.focus()
  }

  render() {
    <View style={styles.dob}>
      {this.refs.map((ref, i) => (
        <>
          <TextInput
            ref={ref}
            numberOfLines={1}
            maxLength={1}
            style={styles.textInputDob}
            keyboardType="numeric"
            placeholder={placeholders[i]}
            onChangeText={number => this.dobHandler(number, i)}
          />
         {(i == 1 || i == 3) && <Text>/</Text>}
       </>        
      ))}
    </View>
  }
}

Since you have a repetitive set, you can use arrays while conditionally inserting the slashes where needed based on index.

like image 107
Avin Kavish Avatar answered Nov 13 '22 22:11

Avin Kavish


You can create a function to return a TextInput with ref, placeholder and num as parameters

renderTextInput(ref, placeholder, num) {
  return (
    <TextInput
      ref={ref}
      numberOfLines={1}
      maxLength={1}
      style={styles.textInputDob}
      keyboardType="numeric"
      placeholder={placeholder}
      onChangeText={number => this.dobHandler(number, num)}
    />
  )
}

Then call it inside the render method

return (
  <View style={styles.dob}>
    {renderTextInput(this.d1, "D", 1))}
    {renderTextInput(this.d2, "D", 2))}
    ...
    ...
  </View>
);
like image 5
Asaf Aviv Avatar answered Nov 13 '22 23:11

Asaf Aviv


Have you considered using Material UI datepicker, and store your DOB as a Date object instead?

If you are using Material v4.x, simply follow the demo. Otherwise, you need to first install material-ui-pickers package. Then,

import { DatePicker, KeyboardDatePicker } from "material-ui-pickers"; // if and only if using Material v3.x

You can then replace your previous implementation with this:

  <KeyboardDatePicker
    autoOk
    variant="inline"
    inputVariant="outlined"
    label="Your DOB"
    format="MM/dd/yyyy"
    placeholder="DD/MM/YYYY" /* use placeholder to guide how your user should enter the date format, in this case, I simply followed your GIF demo */
    value="" /* use a default value or leave it empty */
    InputLabelProps= {{ /* customise style of your label */ }}
    InputProps= {{ /* customise style of how your input should look like */ }}
    InputAdornmentProps={{ position: "start" }}
    onChange={date => handleDateChange(date)}
  />

Your dobHandler will no longer be needed. You will simply update your DOB state value or save your DOB using handleDateChange().

handleDateChange(date){
  /* do something whenever a new date is entered */
}

This should address the followings without your existing dobHandler function, and the long TextField per integer.

I want the focus to moved to the next one, which happens in dobHandler function

Answer: This will be handled by Material UI's datepicker, refer to demo here

like image 2
Arial Avatar answered Nov 14 '22 00:11

Arial