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
相关产品推荐
相关产品推荐

