React中点击按钮获取输入值二次点击textInput为null的问题排查
解决React函数组件中第二次点击按钮时ref为null的问题
这个错误的根源很明确:你在**函数组件中错误地使用了React.createRef()**来获取DOM引用。
为什么会出现这个问题?
React.createRef()的特性是:每次组件重新渲染时,它都会生成一个全新的引用对象。当你点击按钮发起查询后,调用setUsers或setTitle会触发组件重新渲染,此时原来的textInput引用就不再指向输入框DOM元素了——第二次点击时,textInput.current自然就变成了null,也就会抛出TypeError: null is not an object的错误。
正确的解决方案:改用useRef()钩子
在函数组件中,管理DOM引用的正确方式是使用useRef()钩子,它会在组件的整个生命周期内保持同一个引用对象,不会因为组件重渲染而丢失指向。
下面是修改后的完整代码示例:
首先确保导入useRef:
import { useRef, useCallback, useState } from 'react';
替换ref的创建方式:
// 用useRef初始化,传入null作为初始值 const textInput = useRef(null);
输入框的ref绑定保持不变:
<input ref={textInput} type="tel" placeholder="Número de teléfono"></input>
优化后的点击回调(增加空判断和错误处理):
const fetchRequest = useCallback(() => { // 先检查输入框DOM是否存在,避免意外报错 if (!textInput.current) return; const inputValue = textInput.current.value.trim(); // 增加空输入判断,提升用户体验 if (!inputValue) { setTitle('⚠️ 请输入有效的手机号'); return; } database.collection('UsuariosDev').where('Telefono', '==', inputValue).get() .then(response => { const fetchedUsers = []; response.forEach(document => { const fetchedUser = { id: document.id, ...document.data() }; fetchedUsers.push(fetchedUser); console.log(fetchedUser); }); setUsers(fetchedUsers); // 简化标题设置逻辑 setTitle(fetchedUsers.length > 0 ? '✔️' : '❌'); }) // 别忘了添加错误处理,避免查询失败时无反馈 .catch(error => { console.error('查询用户数据失败:', error); setTitle('❌ 查询出错,请稍后重试'); }); }, []); // useRef的引用本身不会变化,所以依赖数组为空即可
额外的优化方案:使用受控组件
如果你想更贴合React的单向数据流模式,也可以用useState来管理输入框的值,完全避免使用ref:
const [phoneNumber, setPhoneNumber] = useState(''); const [users, setUsers] = useState([]); const [title, setTitle] = useState(''); const fetchRequest = useCallback(() => { const trimmedNumber = phoneNumber.trim(); if (!trimmedNumber) { setTitle('⚠️ 请输入手机号'); return; } database.collection('UsuariosDev').where('Telefono', '==', trimmedNumber).get() .then(response => { // ... 同之前的处理逻辑 }) .catch(error => { // ... 错误处理 }); }, [phoneNumber]); // 依赖数组要加上phoneNumber,保证回调能获取最新值
对应的输入框改成受控组件:
<input type="tel" placeholder="Número de teléfono" value={phoneNumber} onChange={(e) => setPhoneNumber(e.target.value)} />
这种方式的优势是可以实时对输入值进行处理(比如格式化手机号),也更符合React的状态管理理念。
内容的提问来源于stack exchange,提问作者David Díaz
相关产品推荐
相关产品推荐

