React Native倒计时导致react-native-otp-inputs输入框失焦怎么修复?
该问题的核心是倒计时状态更新时触发了父组件全量重渲染,连带OTP输入组件也被强制重绘,最终导致输入框失去焦点。即使将倒计时封装为独立组件,如果没有做渲染拦截,或者传递给组件的props存在不必要的引用更新,依然会触发重渲染逻辑。
给倒计时组件添加
React.memo包裹,阻断父组件非相关状态更新带来的重渲染
注意如果给倒计时组件传递了匿名函数、动态生成的对象这类props,会导致React.memo失效,需要将对应的props用useCallback、useMemo做缓存处理。
示例代码:// 用memo包裹倒计时组件,仅props变化时才重渲染 const CountdownBtn = React.memo(({ remainingCount, onResend }) => { return ( <TouchableOpacity disabled={remainingCount > 0} onPress={onResend}> <Text>{remainingCount > 0 ? `${remainingCount}秒后重发` : "获取验证码"}</Text> </TouchableOpacity> ) })为OTP组件做相同的渲染优化,同时缓存所有回调props
给react-native-otp-inputs组件也用React.memo包裹,同时将onCodeChange、onFill等所有回调函数用useCallback缓存,避免每次父组件渲染生成新的函数引用,触发OTP组件重渲染。
示例代码:// 缓存OTP变更回调,依赖项仅填写实际用到的变量 const handleOtpUpdate = useCallback((code) => { setInputOtp(code) }, []) // 渲染OTP组件 <OTPInputs onCodeChange={handleOtpUpdate} // 其余属性保持不变 />将倒计时逻辑完全抽离,减少重渲染影响范围
如果之前仅抽离了倒计时UI,状态还是存放在父组件,父组件依然会因为倒计时状态更新触发重渲染。可以将倒计时逻辑封装为独立的自定义Hook,或者直接把状态放在倒计时组件内部,从根源上避免影响OTP组件。
自定义Hook示例:const useCountdown = (initSeconds = 30) => { const [remaining, setRemaining] = useState(0) const [timerId, setTimerId] = useState(null) const startCountdown = useCallback(() => { setRemaining(initSeconds) const id = setInterval(() => { setRemaining(prev => { if (prev <= 1) { clearInterval(id) return 0 } return prev - 1 }) }, 1000) setTimerId(id) }, [initSeconds]) // 组件卸载时清除定时器 useEffect(() => { return () => timerId && clearInterval(timerId) }, [timerId]) return { remaining, startCountdown } }兜底方案:手动控制输入框焦点
如果上述优化都无法生效,可以通过ref获取react-native-otp-inputs的实例,在必要时手动调用focus()方法恢复焦点,或者给组件添加autoFocus属性保证重渲染后自动聚焦。
打开React DevTools的高亮更新功能,触发倒计时后观察,仅倒计时组件被高亮说明优化生效,OTP组件不会再被触发重渲染,焦点丢失问题自然解决。
内容的提问来源于stack exchange,提问作者Huzaifa Azim

