实现GitHub用户实时搜索(输入触发而非表单提交)
实现GitHub用户实时搜索(无需提交表单)
问题原因
你之前用Debounce没生效,核心问题是组件每次重新渲染都会创建新的防抖函数实例,导致输入事件触发的都是新函数,完全起不到防抖作用。在React里必须用useCallback来缓存防抖函数的引用,确保它在组件生命周期内保持稳定。
修改后的完整代码
import { useState, useCallback } from 'react'; // 可以用lodash的debounce,也可以用下面自己实现的版本 import debounce from 'lodash.debounce'; const GitHubUserSearch = () => { const [input, setInput] = useState(''); const [user, setUser] = useState(null); const [loading, setLoading] = useState(false); const [errorMsg, setErrorMsg] = useState(''); // 缓存防抖后的API请求函数 const searchUser = useCallback( debounce(async (query) => { // 空输入清空结果 if (!query.trim()) { setUser(null); setErrorMsg(''); return; } setLoading(true); setErrorMsg(''); try { // 优先按用户名搜索 let res = await fetch(`https://api.github.com/users/${query}`); if (res.ok) { setUser(await res.json()); return; } // 用户名搜索失败,尝试按邮箱搜索(GitHub API不支持直接邮箱查用户,需通过搜索接口筛选) res = await fetch(`https://api.github.com/search/users?q=${encodeURIComponent(query)}+in:email`); const searchResult = await res.json(); if (searchResult.items?.length > 0) { // 取第一个匹配的用户详情 const userRes = await fetch(`https://api.github.com/users/${searchResult.items[0].login}`); setUser(await userRes.json()); } else { throw new Error('未找到匹配的用户'); } } catch (err) { setErrorMsg(err.message || '获取用户信息失败'); setUser(null); } finally { setLoading(false); } }, 500), // 500ms防抖延迟,可根据需求调整 [] // 空依赖确保防抖函数只创建一次 ); // 输入变化时触发防抖搜索 const handleInputChange = (e) => { const value = e.target.value; setInput(value); searchUser(value); }; return ( <div style={{ maxWidth: 450, margin: '3rem auto', padding: '0 1.5rem' }}> <h2>GitHub 用户实时搜索</h2> <input type="text" value={input} onChange={handleInputChange} placeholder="输入用户名或邮箱" style={{ width: '100%', padding: '0.75rem', fontSize: '1rem', border: '1px solid #ddd', borderRadius: '4px', marginTop: '1rem' }} /> {loading && <p style={{ color: '#666', marginTop: '1rem' }}>加载中...</p>} {errorMsg && <p style={{ color: '#dc3545', marginTop: '1rem' }}>{errorMsg}</p>} {user && ( <div style={{ marginTop: '1.5rem', padding: '1.5rem', border: '1px solid #eee', borderRadius: '6px' }}> <img src={user.avatar_url} alt={`${user.login} 的头像`} style={{ width: 90, height: 90, borderRadius: '50%' }} /> <h3 style={{ margin: '1rem 0' }}>{user.name || user.login}</h3> <p style={{ color: '#666', margin: '0.5rem 0' }}>{user.bio || '暂无个人简介'}</p> <p style={{ margin: '0.5rem 0' }}>公开仓库: {user.public_repos}</p> <p style={{ margin: '0.5rem 0' }}>关注者: {user.followers}</p> </div> )} </div> ); }; // 如果你不想用lodash,可以用这个自定义的debounce函数 /* const debounce = (func, delay) => { let timeoutId; return (...args) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => func(...args), delay); }; }; */ export default GitHubUserSearch;
核心修改说明
- 防抖函数缓存:用
useCallback包裹debounce生成的函数,依赖项设为空数组,保证组件渲染时防抖函数的引用不会改变,真正实现防抖效果。 - API调用逻辑:GitHub官方API不支持直接通过邮箱查询用户,所以先尝试用户名搜索,失败后调用搜索接口筛选包含目标邮箱的用户,再获取用户详情。
- 状态完善:添加了
loading和errorMsg状态,让用户能看到加载状态和错误提示,提升交互体验。 - 输入处理:输入框的
onChange事件直接触发防抖后的搜索函数,无需提交按钮,实现实时搜索。
注意事项
- GitHub API有请求频率限制,未授权用户每小时最多60次请求。如果需要更高限额,可以在请求头里添加授权token:
headers: { Authorization: 'token YOUR_GITHUB_TOKEN' }。 - 防抖延迟时间(500ms)可根据需求调整,太短可能还是会频繁请求,太长会有明显延迟感。
内容的提问来源于stack exchange,提问作者vinit pandey
相关产品推荐
相关产品推荐

