You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中使用useRef在回调外定位输入框光标到精确位置

React 输入框引号自动补全功能异常问题

需求说明

  • 提供 input 输入框供用户输入形如 "TERM":"MATCH_TERM" 的表达式
  • 交互要求:用户输入引号(支持双引号 "、单引号 ')时,自动补全同类型的第二个引号,光标自动定位到两个引号中间
  • 技术栈:ReactJs

当前实现代码

目前已经完成引号自动补全的字符串拼接逻辑,但存在功能异常,可复现问题的简化代码如下:

import * as React from "react";
import { useEffect, useState, useRef } from "react";

const QuoteInput: React.FC = () => {
   
    const inputRef = useRef<HTMLInputElement | null>(null);
                     
    const [inputChoose, setInputChoose] = useState("");
    const [previousInputChoose, setPreviousInputChoose] = useState("");

    const [testQuoteAddition, setTestQuoteAddition] = useState(false);
    const [enterSingleQuote, setEnterSingleQuote] = useState(false);
    const [enterDoubleQuote, setEnterDoubleQuote] = useState(false);

    const inputHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
        setPreviousRequestChoose(requestChoose);
        const enteredRequest = event.target.value;
        setRequestChoose(enteredRequest);

        setTestQuoteAddition(true);
    };

    function addSingleQuote(indexDifference: number) {
        let newString: string = requestChoose.slice(0,indexDifference + 1) + "'" + requestChoose.slice(indexDifference + 1);
        setRequestChoose(newString);

        if(inputRef !== null && inputRef !== undefined) {
            if (inputRef.current !== null && inputRef.current !== undefined) {
                console.log("3 ");
                if (inputRef.current.setSelectionRange !== undefined) {
                    inputRef.current.setSelectionRange(indexDifference, indexDifference);
                }
            }
        }
    }

    function addDoubleQuote(indexDifference: number) {
        let newString: string = requestChoose.slice(0,indexDifference + 1) + '"' + requestChoose.slice(indexDifference + 1);
        setRequestChoose(newString);
    }

    useEffect(()=>{
        if(testQuoteAddition === true) {
            for(let i=0; i<requestChoose.length; i++) {
                if(previousRequestChoose.charAt(i) !== requestChoose.charAt(i))
                {
                    if (requestChoose.charAt(i) === "'") {
                        setEnterSingleQuote(true);
                    } else if (requestChoose.charAt(i) === '"') {
                        setEnterDoubleQuote(true);
                    }
                }
            }
        }
        setTestQuoteAddition(false);
    },[testQuoteAddition])

    useEffect(()=>{
        if(enterSingleQuote === true){
            let indexDifferenceInRequest: number = requestChoose.length + 1;
            let findDifference: boolean = false
            for(let i=0; i<requestChoose.length; i++) {
                if(previousRequestChoose.charAt(i) !== requestChoose.charAt(i) && findDifference === false)
                {
                    indexDifferenceInRequest = i;
                    findDifference = true;
                }
            }
            addSingleQuote(indexDifferenceInRequest);
            setEnterSingleQuote(false);
        } else if (enterDoubleQuote === true){
            let indexDifferenceInRequest: number = requestChoose.length + 1;
            let findDifference: boolean = false
            for(let i=0; i<requestChoose.length; i++) {
                if(previousRequestChoose.charAt(i) !== requestChoose.charAt(i) && findDifference === false)
                {
                    indexDifferenceInRequest = i;
                    findDifference = true;
                }
            }
            addDoubleQuote(indexDifferenceInRequest);
            setEnterDoubleQuote(false);
        }
    },[enterSingleQuote, enterDoubleQuote])

    return(
        <div>
            <input ref={inputRef} type="text" onChange={inputHandler} value={inputChoose} className="text-center" placeholder="enter an input" />
        </div>
    );
}

export default QuoteInput;

异常现象

  • 光标定位不生效:补全引号后光标默认跳到字符串末尾,没有停在两个引号之间。测试验证:把 inputRef.current?.setSelectionRange(3, 3) 写在 inputHandler 回调里时,光标会正常被重置到第三个字符位置;但把 inputRef.current?.setSelectionRange(indexDifference, indexDifference) 写在补全引号的函数中时,光标设置逻辑完全不生效,加日志确认条件判断都正常进入,对应代码确实执行了,但没有效果。
  • 快速输入时补全失效:正常输入速度下引号补全能正常运行,快速输入时补全功能直接失效,就算之后恢复正常输入速度也无法恢复。日志排查发现:快速输入前inputHandler内所有逻辑都正常触发,快速输入后只有存储新旧输入值的状态会更新,用来触发引号校验的布尔状态testQuoteAddition不会被设为true,导致依赖它的useEffect不执行,补全逻辑完全不运行。

内容的提问来源于stack exchange,提问作者vincent

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 23:45:44