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

Next.js集成Hasura过滤查询时搜索结果始终滞后一次按键

问题原因

出现数据滞后一次按键更新,本质是对React状态更新和GraphQL查询钩子的执行时机理解有偏差:

  • React的状态更新是异步批处理的:调用setSearchFilter不会立刻修改当前函数执行上下文中的searchFilter值,新值要等下一次组件重渲染才会生效。
  • useRecipe_Filter这类查询钩子,只会在组件重渲染、拿到新的变量值时才会发起新请求,返回的data也是重渲染后才能拿到最新值。

你在输入事件的回调函数里,刚调用完setSearchFilter就立刻读取data做映射、调用props.setResults,此时拿到的永远是上一次请求返回的旧数据,自然会始终慢一次输入。
另外你把loading、error的返回逻辑写在事件回调里是完全无效的,事件回调里的return不会渲染任何UI内容,加载和错误提示永远不会显示。

修复方案

不要在输入事件回调里处理请求返回的数据,用useEffect监听data、loading、error的变化,在数据更新后再统一处理结果、传给父组件,同时把加载、错误提示移到JSX渲染逻辑中。
修复后的核心代码如下:

const SearchBar = (props: any) => {
  const [searchFilter, setSearchFilter] = useState('');
  const { data, loading, error } = useRecipe_Filter({
    variables: {
      title: `%${searchFilter}%`
    }
  });

  // 输入事件只负责更新输入框状态,不处理请求数据
  const handleInputChange = (e: any) => {
    setSearchFilter(e.target.value);
  };

  // 监听请求返回状态变化,统一处理结果映射
  useEffect(() => {
    if (loading || error) {
      props.setResults([]);
      return;
    }
    let mappedData: any = [];
    if (data?.recipes) {
      mappedData = data.recipes.map((recipe: any) => ({
        id: recipe.id,
        title: recipe.title,
        image: recipe.image,
        ingredients: recipe.ingredient_relation.map((ingredient: any) => ({
          id: ingredient.id,
          name: ingredient.name
        })),
        description: recipe.description
      }));
    }
    props.setResults(mappedData);
  }, [data, loading, error]);

  const handleAddNewRecipe = () => {
    props.onOpen();
    if (props.onSetChangeName(true) || props.onSetShowButton(true)) {
      props.onSetShowButton(false);
      props.onSetChangeName(false);
    }
    props.setFormName('');
    props.setFormImage('');
    props.setFormDescription('');
    props.setFormIngredient('');
  };

  return (
    <div>
      <Container>
        <input
          type="text"
          placeholder="Search for a recipe..."
          value={searchFilter}
          onChange={handleInputChange}
        />
        <button type="button" id="btn" onClick={handleAddNewRecipe}>
          Add new recipe
        </button>
      </Container>
      {loading && <p>Loading...</p>}
      {error && <p>{error.message}</p>}
      {!loading && !error && (
        <RecipeList
          onOpen={props.onOpen}
          recipe={props.results}
          setRecipe={props.setResults}
          onSetShowButton={props.onSetShowButton}
          onSetChangeName={props.onSetChangeName}
          setFormName={props.setFormName}
          setFormImage={props.setFormImage}
          setFormDescription={props.setFormDescription}
          setFormIngredient={props.setFormIngredient}
        />
      )}
    </div>
  );
};
额外优化建议
  • 可以给搜索逻辑增加防抖,避免每次按键都立刻发起请求,减少无效接口调用。比如引入防抖钩子将输入值延迟300ms更新,等用户停止输入后再触发查询。
  • 尽量避免使用any做类型标注,可以基于GraphQL schema自动生成对应的TS类型,提前规避属性拼写、空值访问之类的低级错误。
  • 你当前写的Hasura查询逻辑没有问题,_ilike操作符配合%包裹参数实现大小写不敏感的模糊匹配,写法完全符合Hasura的语法规范。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 06:18:21