admin管理员组

文章数量:1297026

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=".0.1/umd/react.production.min.js"></script>
<script src=".0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

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/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Share edited Feb 11 at 20:09 0stone0 44.3k5 gold badges51 silver badges80 bronze badges asked Feb 11 at 16:37 M. Varun ReddyM. Varun Reddy 337 bronze badges 1
  • 1 Here's a code sandbox preview of the same I am new here and couldnt reproduce the code-snippet on stack-snippets – M. Varun Reddy Commented Feb 11 at 18:09
Add a comment  | 

1 Answer 1

Reset to default 1

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

本文标签: