Search code examples
javascriptreactjsreact-bootstrap

How can I use multiple refs to move focus among inputs?


In a legacy project I have React Bootstrap V3.

I need to move the focus on next input text. E.g. if user has been inserted the number into first input, focus need to be on second input. And goes on.

I know that I need to use the ref, but I cannot understand how use multiple refs.

This is my code:

export default class OtpInput extends React.PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      otp: "12",
    };
  }

  componentDidUpdate() {
    console.log("Update!");
    console.log("this.inputControlRef ################", this.inputControlRef);
  }

  setOtp(e) {
    // Only to invoke the componentDidUpdate()
    this.setState({
      otp: "21",
    });
  }

  render() {
    return (
      <form className="otpInput">
        <Row>
          <Col xs={2}>
            <FormGroup controlId="otpInput1">
              <FormControl
                inputRef={(ref) => {
                  this.inputControlRef = ref;
                }}
                type="text"
                value={
                  this.state && this.state.otp.length > 0 && this.state.otp[0]
                }
                onChange={(e) => this.setOtp(e)}
              />
            </FormGroup>
          </Col>
          <Col xs={2}>
            <FormGroup controlId="otpInput2">
              <FormControl
                inputRef={(ref) => {
                  this.inputControlRef = ref;
                }}
                type="text"
                value={
                  this.state && this.state.otp.length > 1 && this.state.otp[1]
                }
                onChange={(e) => this.setOtp(e)}
              />
            </FormGroup>
          </Col>
        </Row>
      </form>
    );
  }
}

With my code in inputControllerRef I have only the last one input text.

I tried

<FormControl
                inputRef={(ref) => {
                  this.inputControlRef[0] = ref;
                }}
                type="text"
                value={
                  this.state && this.state.otp.length > 0 && this.state.otp[0]
                }
                onChange={(e) => this.setOtp(e)}
              />

at the first input but I got Uncaught TypeError: Cannot set properties of undefined (setting '0')


Solution

  • In Class Based components

    Define something like that in the constructor

    constructor() {
      super();
      this.formControl = [];
    }
    

    Add your components like below

    <FormControl
      inputRef={(el) => this.formControl[0] = el}
      type="text"
      value={this.state && this.state.otp.length > 0 && this.state.otp[0]}
      onChange={(e) => this.setOtp(e)}
    />
    <FormControl
      inputRef={(el) => this.formControl[1] = el}
      type="text"
      value={this.state && this.state.otp.length > 1 && this.state.otp[1]}
      onChange={(e) => this.setOtp(e)}
    />
    

    Access the first one this way

    componentDidUpdate() {
      console.log("Update!");
      console.log("First Form Control", this.formControl[0]);
    }
    

    Take this as reference: https://codesandbox.io/s/gw6rp2?file=/App.js&utm_medium=sandpack