React+GraphQL项目报getUserLocation is not a function与400错误
问题根因
两个报错均为Apollo Client使用方式错误导致,具体问题点:
useQuery是React Hook,执行后返回的是包含data/loading/error的状态对象,不是可执行函数,把它当函数调用就会触发getUserlocation is not a function报错- GraphQL查询语句未声明
location入参,前端传参时后端无法识别参数定义,直接返回400 Bad Request - 混淆了
useQuery(组件挂载时自动执行查询)和useLazyQuery(手动触发查询)的适用场景:搜索功能需要用户输入关键词、点击提交后才执行查询,不适合用useQuery - 自定义Hook逻辑错误,且未在组件中正确引入,反而在组件里直接解构
useQuery返回值中不存在的getLocation方法;同时把Apollo返回结果当成fetch风格的Response对象做解析,逻辑完全不匹配。
修正方案
1. 修正GraphQL查询定义(queries.js)
给查询加上location入参声明,和后端接收的参数名保持一致:
import { gql } from "@apollo/client"; export const QUERY_GET_LOCATIONS = gql` query getUserLocation($location: String!) { getUserLocation(location: $location) { _id username bio } } `;
注意:如果后端接口的location参数不是必填项,把
String!里的感叹号去掉即可,常规搜索逻辑下该参数设为必填。
2. 移除无效的自定义Hook
之前写的useUserLocation自定义Hook逻辑完全错误,直接删除即可,不需要保留。
3. 修正SearchMenu.js组件逻辑
把错误的useQuery用法替换为useLazyQuery实现手动触发查询,移除fetch风格的响应解析逻辑,Apollo会直接返回结构化的查询结果:
import React, { useState } from "react"; import { Jumbotron, Container, Col, Form, Button, Card, CardColumns, } from "react-bootstrap"; import { QUERY_GET_LOCATIONS } from "../util/queries"; import { useLazyQuery } from "@apollo/client"; const SearchedUsers = () => { const [searchedUsers, setSearchedUsers] = useState([]); const [searchInput, setSearchInput] = useState(""); // useLazyQuery返回查询触发函数和查询状态,第一个返回值就是可手动调用的查询方法 const [getLocation, { loading }] = useLazyQuery(QUERY_GET_LOCATIONS); const handleFormSubmit = async (event) => { event.preventDefault(); if (!searchInput) { return false; } try { // 触发查询,传入location变量 const { data } = await getLocation({ variables: { location: searchInput } }); // 直接从data字段取后端返回数据,不需要调用.json()、也不需要判断response.ok const userData = data?.getUserLocation?.map((user) => ({ userId: user._id, userName: user.username || "No user to display", bio: user.bio || "No bio available" })) || []; setSearchedUsers(userData); setSearchInput(""); } catch (err) { console.error(err); } }; return ( <> <Jumbotron fluid className="text-light bg-dark"> <Container> <h1>Search for Location!</h1> <Form onSubmit={handleFormSubmit}> <Form.Row> <Col xs={12} md={8}> <Form.Control name="searchInput" value={searchInput} onChange={(e) => setSearchInput(e.target.value)} type="text" size="lg" placeholder="Search for a location" /> </Col> <Col xs={12} md={4}> <Button type="submit" variant="success" size="lg" disabled={loading}> {loading ? "Searching..." : "Submit Search"} </Button> </Col> </Form.Row> </Form> </Container> </Jumbotron> <Container> <h2> {searchedUsers.length ? `Viewing ${searchedUsers.length} results:` : "Search for a location to begin"} </h2> <CardColumns> {searchedUsers.map((user) => { return ( <Card key={user.userId} border="dark"> <Card.Body> <Card.Title>{user.userName}</Card.Title> <p className="small">Bio: {user.bio}</p> </Card.Body> </Card> ); })} </CardColumns> </Container> </> ); }; export default SearchedUsers;
补充说明
- 所有Apollo Hook返回的查询结果都不是fetch风格的Response对象,不要用
response.ok或者response.json()做解析,直接从返回结果的data字段取后端返回的数据即可 - 带参数的GraphQL查询必须先在gql语句中声明参数的名称、类型,再传入对应查询字段中,否则后端会直接返回400错误
- 需要用户交互后才触发的查询,统一用
useLazyQuery实现,不要基于useQuery手动封装调用逻辑,很容易触发Hook调用顺序错误的问题。
内容的提问来源于stack exchange,提问作者FaustoLopez
相关产品推荐
相关产品推荐

