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
相关产品推荐
相关产品推荐

