Next.js使用useQuery报watchQuery未定义错误,求助排查依赖问题
解决Next.js中useQuery报错"cannot read properties of undefined (reading 'watchQuery')"
我有一个Next.js应用,调用useQuery时触发错误:cannot read properties of undefined (reading 'watchQuery') at useQuery。之前为了解决Hook渲染错误,多次调整next和apollo-client的版本,怀疑是依赖或配置问题,相关文件如下:
package.json
{ "dependencies": { "@apollo/client": "3.6.2", "@apollo/link-error": "^2.0.0-beta.3", "@apollo/react-ssr": "^4.0.0", "@keystone-6/document-renderer": "^1.1.2", "@sentry/nextjs": "^7.52.1", "@stripe/react-stripe-js": "^1.1.2", "@stripe/stripe-js": "^1.11.0", "apollo-upload-client": "^14.1.3", "babel-core": "^7.0.0-bridge.0", "babel-plugin-macros": "^3.1.0", "babel-plugin-styled-components": "^2.1.1", "date-fns": "^2.16.1", "downshift": "^6.0.12", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.5", "graphql": "15.8.0", "graphql-tag": "^2.11.0", "graphql-upload": "^11.0.0", "lodash.debounce": "^4.0.8", "next": "^12.2.2", "next-with-apollo": "^5.1.1", "nprogress": "^0.2.0", "prop-types": "^15.7.2", "react": "^17.0.2", "react-dom": "^17.0.2", "react-is": "^17.0.2", "react-transition-group": "^4.4.1", "styled-components": "^5.2.1", "vercel": "^29.3.4", "waait": "^1.0.5" }, "devDependencies": { "@apollo/react-testing": "^4.0.0", "@babel/core": "^7.12.10", "@babel/preset-env": "^7.12.11", "@testing-library/jest-dom": "^5.11.8", "@testing-library/react": "^11.2.3", "@testing-library/user-event": "^12.6.0", "babel-eslint": "^10.1.0", "babel-jest": "^26.6.3", "babel-plugin-styled-components": "^2.1.1", "casual": "^1.6.2", "eslint": "^7.17.0", "eslint-config-airbnb": "^18.2.1", "eslint-config-prettier": "^7.1.0", "eslint-config-wesbos": "^1.0.1", "eslint-plugin-html": "^6.1.1", "eslint-plugin-import": "^2.22.1", "eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-prettier": "^3.3.1", "eslint-plugin-react": "^7.22.0", "eslint-plugin-react-hooks": "^4.2.0", "jest": "^26.6.3", "prettier": "^2.2.1", "react-test-renderer": "^17.0.1" }, "engines": { "node": "16.x" }, "//": "This is our babel config, I prefer this over a .babelrc file", "babel": { "env": { "development": { "presets": [ "next/babel" ], "plugins": [ [ "babel-plugin-styled-components", "macros", { "ssr": true, "displayName": true } ] ] }, "production": { "presets": [ "next/babel" ], "plugins": [ [ "babel-plugin-styled-components", "macros", { "ssr": true, "displayName": true } ] ] }, "test": { "presets": [ [ "next/babel", { "preset-env": { "modules": "commonjs" } } ] ], "plugins": [ [ "babel-plugin-styled-components", { "ssr": true, "displayName": true } ] ] } } } }
createClient代码(withData.js)
import { ApolloClient, ApolloLink, InMemoryCache } from "@apollo/client"; import { onError } from "@apollo/link-error"; import { createUploadLink } from "apollo-upload-client"; import withApollo from "next-with-apollo"; import { prodEndpoint, cors } from "../config"; function createClient({ headers, initialState }) { return new ApolloClient({ link: ApolloLink.from([ onError(({ graphQLErrors, networkError }) => { if (graphQLErrors) graphQLErrors.forEach(({ message, locations, path }) => console.log( `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}` ) ); if (networkError) console.log( `[Network error]: ${networkError}. Backend is unreachable. Is it running?` ); }), createUploadLink({ uri: prodEndpoint, cors, fetchOptions: { credentials: "include", }, headers, }), ]), cache: new InMemoryCache().restore(initialState || {}), }); } export default withApollo(createClient);
_app.js代码
import { ApolloProvider } from "@apollo/client"; import NProgress from "nprogress"; import Router from "next/router"; import Page from "../components/Page"; import "../components/styles/nprogress.css"; import apolloClient from "../lib/withData"; Router.events.on("routeChangeStart", () => NProgress.start()); Router.events.on("routeChangeComplete", () => NProgress.done()); Router.events.on("routeChangeError", () => NProgress.done()); function MyApp({ Component, pageProps }) { // const router = useRouter(); return ( <ApolloProvider client={apolloClient}> <Page> <Component {...pageProps} /> </Page> </ApolloProvider> ); } MyApp.getInitialProps = async function ({ Component, ctx }) { let pageProps = {}; console.log(pageProps); if (Component.getInitialProps) { console.log(`Paging all props ${pageProps}`); pageProps = await Component.getInitialProps(ctx); } pageProps.query = ctx.query; return { pageProps }; }; export default MyApp;
问题分析
- 核心配置错误:
_app.js中错误地将next-with-apollo返回的高阶组件直接传给ApolloProvider的client属性。next-with-apollo的作用是通过高阶组件为应用注入ApolloClient实例,而非直接导出client对象,导致ApolloProvider未拿到有效的client实例,useQuery无法找到client从而报错。 - 冗余/冲突依赖:
@apollo/react-ssr是Apollo Client v2的SSR包,而当前使用的@apollo/client@3.6.2已经内置了SSR功能,两者共存会导致版本冲突;@apollo/react-testing同理,v3版本应使用@apollo/client/testing。
解决方案
步骤1:修正_app.js的ApolloProvider使用方式
next-with-apollo返回的是高阶组件,需要用它包裹你的MyApp组件,而非直接引用apolloClient作为client参数。修改后的_app.js如下:
import { ApolloProvider } from "@apollo/client"; import NProgress from "nprogress"; import Router from "next/router"; import Page from "../components/Page"; import "../components/styles/nprogress.css"; import withApollo from "../lib/withData"; Router.events.on("routeChangeStart", () => NProgress.start()); Router.events.on("routeChangeComplete", () => NProgress.done()); Router.events.on("routeChangeError", () => NProgress.done()); function MyApp({ Component, pageProps, apolloClient }) { return ( <ApolloProvider client={apolloClient}> <Page> <Component {...pageProps} /> </Page> </ApolloProvider> ); } MyApp.getInitialProps = async function ({ Component, ctx }) { let pageProps = {}; if (Component.getInitialProps) { pageProps = await Component.getInitialProps(ctx); } pageProps.query = ctx.query; return { pageProps }; }; // 使用next-with-apollo高阶组件包裹MyApp export default withApollo(MyApp);
步骤2:清理冲突依赖
更新package.json,移除冗余的旧版Apollo依赖,调整后相关依赖如下:
"dependencies": { "@apollo/client": "3.6.2", "@apollo/client/link/error": "^3.6.2", // 替换@apollo/link-error beta版 "@keystone-6/document-renderer": "^1.1.2", "@sentry/nextjs": "^7.52.1", "@stripe/react-stripe-js": "^1.1.2", "@stripe/stripe-js": "^1.11.0", "apollo-upload-client": "^14.1.3", "babel-core": "^7.0.0-bridge.0", "babel-plugin-macros": "^3.1.0", "babel-plugin-styled-components": "^2.1.1", "date-fns": "^2.16.1", "downshift": "^6.0.12", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.5", "graphql": "15.8.0", "graphql-tag": "^2.11.0", "graphql-upload": "^11.0.0", "lodash.debounce": "^4.0.8", "next": "^12.2.2", "next-with-apollo": "^5.1.1", "nprogress": "^0.2.0", "prop-types": "^15.7.2", "react": "^17.0.2", "react-dom": "^17.0.2", "react-is": "^17.0.2", "react-transition-group": "^4.4.1", "styled-components": "^5.2.1", "vercel": "^29.3.4", "waait": "^1.0.5" }, "devDependencies": { "@apollo/client/testing": "^3.6.2", // 替换@apollo/react-testing "@babel/core": "^7.12.10", "@babel/preset-env": "^7.12.11", "@testing-library/jest-dom": "^5.11.8", "@testing-library/react": "^11.2.3", "@testing-library/user-event": "^12.6.0", "babel-eslint": "^10.1.0", "babel-jest": "^26.6.3", "babel-plugin-styled-components": "^2.1.1", "casual": "^1.6.2", "eslint": "^7.17.0", "eslint-config-airbnb": "^18.2.1", "eslint-config-prettier": "^7.1.0", "eslint-config-wesbos": "^1.0.1", "eslint-plugin-html": "^6.1.1", "eslint-plugin-import": "^2.22.1", "eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-prettier": "^3.3.1", "eslint-plugin-react": "^7.22.0", "eslint-plugin-react-hooks": "^4.2.0", "jest": "^26.6.3", "prettier": "^2.2.1", "react-test-renderer": "^17.0.1" }
步骤3:重新安装依赖
执行以下命令清理并重新安装依赖:
rm -rf node_modules package-lock.json yarn.lock npm install # 或 yarn install
内容的提问来源于stack exchange,提问作者max
相关产品推荐
相关产品推荐

