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

实现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;

核心修改说明

  1. 防抖函数缓存:用useCallback包裹debounce生成的函数,依赖项设为空数组,保证组件渲染时防抖函数的引用不会改变,真正实现防抖效果。
  2. API调用逻辑:GitHub官方API不支持直接通过邮箱查询用户,所以先尝试用户名搜索,失败后调用搜索接口筛选包含目标邮箱的用户,再获取用户详情。
  3. 状态完善:添加了loading和errorMsg状态,让用户能看到加载状态和错误提示,提升交互体验。
  4. 输入处理:输入框的onChange事件直接触发防抖后的搜索函数,无需提交按钮,实现实时搜索。

注意事项

  • GitHub API有请求频率限制,未授权用户每小时最多60次请求。如果需要更高限额,可以在请求头里添加授权token:headers: { Authorization: 'token YOUR_GITHUB_TOKEN' }。
  • 防抖延迟时间(500ms)可根据需求调整,太短可能还是会频繁请求,太长会有明显延迟感。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 04:50:08