React中调用.focus()提示not a function错误,求解决及优化方案
Hey there! Let's break down why your OTP input focus switching isn't working and how to fix it, plus some better practices for this common use case.
The Root Cause of the Error
You're using React.createRef() to reference your custom OtpField components, but by default, React doesn't pass refs through to the underlying DOM element (like the <input> inside OtpField). That means fieldOne.current is actually the instance of the OtpField component, not the input node—so calling .focus() on it throws the "is not a function" error.
Step 1: Forward Refs to the Input in OtpField
First, you need to update your OtpField component to forward the ref it receives to the internal input element using React.forwardRef:
// Inside OtpField.js or wherever your component is defined const OtpField = React.forwardRef((props, ref) => { // Pass the ref directly to your input element return <input ref={ref} {...props} />; }); export default OtpField;
This makes sure that when you attach a ref to OtpField in your parent component, it points straight to the actual input DOM node, so .focus() will work as expected.
Step 2: Simplify Focus Switching Logic
Your current approach of manually linking each field's onChange to the next ref works, but it's repetitive. Let's clean this up with an array of refs and a reusable handler:
import React, { useRef } from 'react'; // ... your other imports const SignUpVerification = () => { // Create an array of refs for all OTP fields const otpRefs = useRef([ React.createRef(), React.createRef(), React.createRef(), React.createRef() ]); // Reusable handler to switch focus between fields const handleFocusSwitch = (index, e) => { // Limit each field to 1 character if (e.target.value.length > 1) return; // Move to next field if current has a value if (e.target.value && index < otpRefs.current.length - 1) { otpRefs.current[index + 1].current.focus(); } // Move to previous field if current is cleared if (!e.target.value && index > 0) { otpRefs.current[index - 1].current.focus(); } }; return ( <div style={{ display: 'flex' }}> <FormDiv style={{ textAlign: 'center' }}> <Nunito32 style={{ display: 'inline-block', textAlign: 'center', marginRight: 236, marginLeft: 200 }} > Verify your mobile number by entering the code we sent you </Nunito32> {/* Add display: flex to make flexDirection: row work */} <div style={{ flexDirection: 'row', display: 'flex' }}> {otpRefs.current.map((ref, index) => ( <OtpField key={index} ref={ref} style={{ marginRight: 10.5 }} onChange={(e) => handleFocusSwitch(index, e)} maxLength={1} // Enforce single character input /> ))} </div> <PinkButton style={{ marginTop: 75 }}>Continue</PinkButton> <Nunito20>Send again</Nunito20> </FormDiv> <ImageContainer> <ImageDiv bg={signup} src={signup} alt="logo" /> </ImageContainer> </div> ); }; export default SignUpVerification;
This adds a few nice improvements:
- Automatic focus switching: Moves to the next field when a character is entered, and back to the previous when deleted
- Input validation: Limits each field to exactly one character with
maxLengthand a check in the handler - Cleaner code: No more repetitive onChange handlers for each field
Bonus: Manage OTP State
If you want to collect the OTP value for submission, add a state variable to track each field's value:
import React, { useRef, useState } from 'react'; // ... const SignUpVerification = () => { const [otpValue, setOtpValue] = useState(new Array(4).fill('')); const otpRefs = useRef([/* ... refs ... */]); const handleFocusSwitch = (index, e) => { const newOtp = [...otpValue]; newOtp[index] = e.target.value.slice(0, 1); setOtpValue(newOtp); // ... existing focus logic ... }; // Later, when submitting: const handleSubmit = () => { const fullOtp = otpValue.join(''); console.log('Submitting OTP:', fullOtp); // Call your API with fullOtp here }; // Update OtpField to use the state value: <OtpField key={index} ref={ref} style={{ marginRight: 10.5 }} onChange={(e) => handleFocusSwitch(index, e)} maxLength={1} value={otpValue[index]} /> };
This makes it easy to access the full OTP string when the user clicks "Continue".
内容的提问来源于stack exchange,提问作者SImon Haddad

