Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to overwrite a current character inside input box (OTP inputs) when it is already full?

I am trying to create an input box for a timer/OTP styled input.

How to overwrite a current character inside input box w length 1 (OTP inputs) when it is already full?

My problem is that the input is length 1, and we move the cursor/focus to the next only after catching a single character... but once a char is filled, you have to manually backspace and delete the char and then you can refill...

currently inside input box(0) but since it is already filled, I can't overwrite it

Code Sandbox Example

const { useState, useRef } = React;

function App() {
    const [time, setTime] = useState(null);
    const [running, setRunning] = useState(false);
    const [editState, setEditState] = useState(false);
    const [hms, setHms] = useState(Array(6).fill(0));
    const inputs = useRef([]);

    const handleChange = (e, idx) => {
        const {value} = e.target;
        if (value.match(/^\d$/)) {
            const newHms = [...hms];
            newHms[idx] = value;
            setHms(newHms);

            if (idx < 6) {
                inputs.current[idx+1].focus();
            }

            if (value === '') {
                if (idx > 0) {
                    inputs.current[idx - 1].focus();
                }
            }
        }
    }

    const handleKeyDown = (e, idx) => {
        if (e.key === 'Backspace') {
            if (hms[idx] != '') {
                const newHms = [...hms];
                newHms[idx] = '';
                setHms(newHms);
                return;
            }

            if (idx > 0) {
                inputs.current[idx - 1].focus();
            }
        }
    }
    return (
        <>
            <div id='root' style={{backgroundColor: "#e3b23c", width:"400px", height:"400px"}}>
                {true && <div>
                    {
                        hms.map((_, idx) => {
                            return (
                                <span>
                                    <input
                                        key={idx}
                                        type='text'
                                        maxLength="1"
                                        value={hms[idx]}
                                        onKeyDown={(e) => handleKeyDown(e, idx)}
                                        onChange={(e) => handleChange(e, idx)}
                                        ref={(el) => (inputs.current[idx] = el)}
                                        style={{width: '40px',
                                            height: '40px',
                                            margin: '0 5px',
                                            textAlign: 'center',
                                            fontSize: '18px',
                                            border: '1px solid #ccc',
                                            borderRadius: '4px'}
                                        }
                                        ></input>
                                        {(idx == 1 || idx == 3) && <span style={{
                                            fontWeight: 600,
                                            fontSize: "30px"
                                        }}>:</span>}
                                </span>
                            );
                        })
                    }
                </div>}
                <div>
                    Hours : Minutes : Seconds
                </div>
            </div>
        </>
    )
}

ReactDOM.render(<App />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
like image 765
M. Varun Reddy Avatar asked Dec 17 '25 21:12

M. Varun Reddy


1 Answers

Your input is already filled, and unless you select the input value and then update the value, changes will not be triggered and hence onChange will not be triggered.

So, you have to trigger this change manually, and although onChange is only called on change, onKeyDown will be called on every keydown even if there is no actualy change of value.

So, the desired behaviour is achievable with the small tweak of moving all your logic from onChange handler to onKeyDown handler.

 const handleChange = (e, idx) => {
  };

  const handleKeyDown = (e, idx) => {
    const keyValue = e.key;
    if (keyValue === "Backspace") {
      if (hms[idx] != "") {
        const newHms = [...hms];
        newHms[idx] = "";
        setHms(newHms);
        return;
      }

      if (idx > 0) {
        inputs.current[idx - 1].focus();
      }
    } else if (keyValue.match(/^\d$/)) {
      const newHms = [...hms];
      newHms[idx] = keyValue;
      setHms(newHms);

      if (idx < 5) {
        inputs.current[idx + 1].focus();
      }

    }
  };

Here is the codesandbox

like image 189
Tushar Shahi Avatar answered Dec 19 '25 12:12

Tushar Shahi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!