Reactjs 接下来使用apollo,SSR不起作用,请求在客户端完成
我有很短的代码,我正在尝试使用,接下来是apolo。 但是SSR在我的情况下不起作用,我还在打客户电话,也许有人可以指导我 我的阿波罗之旅->Reactjs 接下来使用apollo,SSR不起作用,请求在客户端完成,reactjs,next.js,apollo-client,Reactjs,Next.js,Apollo Client,我有很短的代码,我正在尝试使用,接下来是apolo。 但是SSR在我的情况下不起作用,我还在打客户电话,也许有人可以指导我 我的阿波罗之旅-> import withApollo from "next-with-apollo"; import ApolloClient, { InMemoryCache } from "apollo-boost"; import { ApolloProvider } from "@apollo/react-hook
import withApollo from "next-with-apollo";
import ApolloClient, { InMemoryCache } from "apollo-boost";
import { ApolloProvider } from "@apollo/react-hooks";
export default withApollo(
({ initialState }) => {
return new ApolloClient({
uri: "http://localhost:4000/graphql",
credentials: "include",
cache: new InMemoryCache().restore(initialState || {}),
});
},
{
render: ({ Page, props }) => {
return (
<ApolloProvider client={props.apollo}>
<Page {...props} />
</ApolloProvider>
);
},
}
);
从“阿波罗下一步”导入阿波罗;
从“apollo boost”导入apollo客户端,{InMemoryCache};
从“@apollo/react hooks”导入{apollo provider}”;
使用Apollo导出默认值(
({initialState})=>{
返回新客户端({
uri:“http://localhost:4000/graphql",
证书:“包括”,
缓存:新建InMemoryCache().restore(初始状态| |{}),
});
},
{
呈现:({Page,props})=>{
返回(
);
},
}
);
而页面本身
import gql from "graphql-tag";
import { useQuery } from "@apollo/react-hooks";
import withApollo from "../../HOC/withApollo";
import { getDataFromTree } from "@apollo/react-ssr";
const QUERY = gql`
{
me {
user {
email
}
}
}
`;
const Profile = () => {
const { data } = useQuery(QUERY);
console.log(data);
return <div>{data?.me.user?.email}</div>;
};
export default withApollo(Profile, { getDataFromTree });
从“graphql标签”导入gql;
从“@apollo/react hooks”导入{useQuery}”;
从“../../HOC/withApollo”导入withApollo;
从“@apollo/react-ssr”导入{getDataFromTree}”;
const QUERY=gql`
{
我{
使用者{
电子邮件
}
}
}
`;
常量配置文件=()=>{
const{data}=useQuery(查询);
控制台日志(数据);
返回{data?.me.user?.email};
};
使用Apollo导出默认值(配置文件,{getDataFromTree});
但是请求仍在客户端执行。请求在客户端被调用,因为您已经编写了
const{data}=useQuery(查询)配置文件组件中的代码>。所以,当组件在客户端呈现时,就会调用该查询
如果只想在服务器端(即ssr)调用该查询,则使用getServerSideProps
方法,在该方法中调用给定的查询,并将结果作为props传递给概要文件组件
例如:
const Profile = ({ data }) => {
// Render data...
}
// This gets called on every request
export async function getServerSideProps() {
// Fetch data from external API
const res = await fetch(`https://.../data`)
const data = await res.json()
// Pass data to the page via props
return { props: { data } }
}
export default Profile;
注意:您可以导入顶级作用域中的模块,以便在getServerSideProps中使用。getServerSideProps中使用的导入不会绑定到客户端。
这意味着您可以直接在getServerSideProps中编写服务器端代码。这包括从文件系统或数据库读取数据。这是我正在使用的解决方案。它允许在HOC中包装页面(主要组件),然后发出请求ssr或notssr
src
│
│
│
└───pages
│ │ _app.tsx
│ │ _document.tsx
| | index.tsx
│ │
│ |
└───apollo
│ withApollo.ts
│ createWithApollo.tsx
createWithApollo.tsx
import React from "react";
import App from "next/app";
import Head from "next/head";
import { ApolloProvider } from "@apollo/client";
// On the client, we store the Apollo Client in the following variable.
// This prevents the client from reinitializing between page transitions.
let globalApolloClient: any = null;
/**
* Installs the Apollo Client on NextPageContext
* or NextAppContext. Useful if you want to use apolloClient
* inside getStaticProps, getStaticPaths or getServerSideProps
* @param {NextPageContext | NextAppContext} ctx
*/
export const initOnContext = (ac: any, ctx: any) => {
const inAppContext = Boolean(ctx.ctx);
// We consider installing `withApollo({ ssr: true })` on global App level
// as antipattern since it disables project wide Automatic Static Optimization.
if (process.env.NODE_ENV === "development") {
if (inAppContext) {
console.warn(
"Warning: You have opted-out of Automatic Static Optimization due to `withApollo` in `pages/_app`.\n" +
"Read more: https://err.sh/next.js/opt-out-auto-static-optimization\n"
);
}
}
// Initialize ApolloClient if not already done
const apolloClient =
ctx.apolloClient ||
initApolloClient(ac, ctx.apolloState || {}, inAppContext ? ctx.ctx : ctx);
// We send the Apollo Client as a prop to the component to avoid calling initApollo() twice in the server.
// Otherwise, the component would have to call initApollo() again but this
// time without the context. Once that happens, the following code will make sure we send
// the prop as `null` to the browser.
apolloClient.toJSON = () => null;
// Add apolloClient to NextPageContext & NextAppContext.
// This allows us to consume the apolloClient inside our
// custom `getInitialProps({ apolloClient })`.
ctx.apolloClient = apolloClient;
if (inAppContext) {
ctx.ctx.apolloClient = apolloClient;
}
return ctx;
};
/**
* Always creates a new apollo client on the server
* Creates or reuses apollo client in the browser.
* @param {NormalizedCacheObject} initialState
* @param {NextPageContext} ctx
*/
const initApolloClient = (apolloClient: any, initialState: any, ctx: any) => {
// Make sure to create a new client for every server-side request so that data
// isn't shared between connections (which would be bad)
if (typeof window === "undefined") {
return createApolloClient(apolloClient(ctx), initialState, ctx);
}
// Reuse client on the client-side
if (!globalApolloClient) {
globalApolloClient = createApolloClient(
apolloClient(ctx),
initialState,
ctx
);
}
return globalApolloClient;
};
/**
* Creates a withApollo HOC
* that provides the apolloContext
* to a next.js Page or AppTree.
* @param {Object} withApolloOptions
* @param {Boolean} [withApolloOptions.ssr=false]
* @returns {(PageComponent: ReactNode) => ReactNode}
*/
export const createWithApollo = (ac: any) => {
return ({ ssr = false } = {}) => (PageComponent: any) => {
const WithApollo = ({ apolloClient, apolloState, ...pageProps }: any) => {
let client;
if (apolloClient) {
// Happens on: getDataFromTree & next.js ssr
client = apolloClient;
} else {
// Happens on: next.js csr
client = initApolloClient(ac, apolloState, undefined);
}
return (
<ApolloProvider client={client}>
<PageComponent {...pageProps} />
</ApolloProvider>
);
};
// Set the correct displayName in development
if (process.env.NODE_ENV !== "production") {
const displayName =
PageComponent.displayName || PageComponent.name || "Component";
WithApollo.displayName = `withApollo(${displayName})`;
}
if (ssr || PageComponent.getInitialProps) {
WithApollo.getInitialProps = async (ctx: any) => {
const inAppContext = Boolean(ctx.ctx);
const { apolloClient } = initOnContext(ac, ctx);
// Run wrapped getInitialProps methods
let pageProps = {};
if (PageComponent.getInitialProps) {
pageProps = await PageComponent.getInitialProps(ctx);
} else if (inAppContext) {
pageProps = await App.getInitialProps(ctx);
}
// Only on the server:
if (typeof window === "undefined") {
const { AppTree } = ctx;
// When redirecting, the response is finished.
// No point in continuing to render
if (ctx.res && ctx.res.finished) {
return pageProps;
}
// Only if dataFromTree is enabled
if (ssr && AppTree) {
try {
// Import `@apollo/react-ssr` dynamically.
// We don't want to have this in our client bundle.
const { getDataFromTree } = await import(
"@apollo/client/react/ssr"
);
// Since AppComponents and PageComponents have different context types
// we need to modify their props a little.
let props;
if (inAppContext) {
props = { ...pageProps, apolloClient };
} else {
props = { pageProps: { ...pageProps, apolloClient } };
}
// Take the Next.js AppTree, determine which queries are needed to render,
// and fetch them. This method can be pretty slow since it renders
// your entire AppTree once for every query. Check out apollo fragments
// if you want to reduce the number of rerenders.
// https://www.apollographql.com/docs/react/data/fragments/
await getDataFromTree(<AppTree {...props} />);
} catch (error) {
// Prevent Apollo Client GraphQL errors from crashing SSR.
// Handle them in components via the data.error prop:
// https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-query-data-error
console.error("Error while running `getDataFromTree`", error);
}
// getDataFromTree does not call componentWillUnmount
// head side effect therefore need to be cleared manually
Head.rewind();
}
}
return {
...pageProps,
// Extract query data from the Apollo store
apolloState: apolloClient.cache.extract(),
// Provide the client for ssr. As soon as this payload
// gets JSON.stringified it will remove itself.
apolloClient: ctx.apolloClient,
};
};
}
return WithApollo;
};
};
function createApolloClient(apolloClient: any, initialState: any, ctx: any) {
// The `ctx` (NextPageContext) will only be present on the server.
// use it to extract auth headers (ctx.req) or similar.
apolloClient.ssrMode = Boolean(ctx);
apolloClient.cache.restore(initialState);
return apolloClient;
}
import Router from "next/router";
import { AppProps } from "next/app";
function MyApp({ Component, pageProps }: AppProps) {
return (
<Component {...pageProps} />
);
}
export default MyApp;
import React from "react";
import { withApollo } from "../apollo/withApollo";
const Index = () => <div></div>;
export default withApollo({ ssr: true })(Index);
\u app.tsx
import React from "react";
import App from "next/app";
import Head from "next/head";
import { ApolloProvider } from "@apollo/client";
// On the client, we store the Apollo Client in the following variable.
// This prevents the client from reinitializing between page transitions.
let globalApolloClient: any = null;
/**
* Installs the Apollo Client on NextPageContext
* or NextAppContext. Useful if you want to use apolloClient
* inside getStaticProps, getStaticPaths or getServerSideProps
* @param {NextPageContext | NextAppContext} ctx
*/
export const initOnContext = (ac: any, ctx: any) => {
const inAppContext = Boolean(ctx.ctx);
// We consider installing `withApollo({ ssr: true })` on global App level
// as antipattern since it disables project wide Automatic Static Optimization.
if (process.env.NODE_ENV === "development") {
if (inAppContext) {
console.warn(
"Warning: You have opted-out of Automatic Static Optimization due to `withApollo` in `pages/_app`.\n" +
"Read more: https://err.sh/next.js/opt-out-auto-static-optimization\n"
);
}
}
// Initialize ApolloClient if not already done
const apolloClient =
ctx.apolloClient ||
initApolloClient(ac, ctx.apolloState || {}, inAppContext ? ctx.ctx : ctx);
// We send the Apollo Client as a prop to the component to avoid calling initApollo() twice in the server.
// Otherwise, the component would have to call initApollo() again but this
// time without the context. Once that happens, the following code will make sure we send
// the prop as `null` to the browser.
apolloClient.toJSON = () => null;
// Add apolloClient to NextPageContext & NextAppContext.
// This allows us to consume the apolloClient inside our
// custom `getInitialProps({ apolloClient })`.
ctx.apolloClient = apolloClient;
if (inAppContext) {
ctx.ctx.apolloClient = apolloClient;
}
return ctx;
};
/**
* Always creates a new apollo client on the server
* Creates or reuses apollo client in the browser.
* @param {NormalizedCacheObject} initialState
* @param {NextPageContext} ctx
*/
const initApolloClient = (apolloClient: any, initialState: any, ctx: any) => {
// Make sure to create a new client for every server-side request so that data
// isn't shared between connections (which would be bad)
if (typeof window === "undefined") {
return createApolloClient(apolloClient(ctx), initialState, ctx);
}
// Reuse client on the client-side
if (!globalApolloClient) {
globalApolloClient = createApolloClient(
apolloClient(ctx),
initialState,
ctx
);
}
return globalApolloClient;
};
/**
* Creates a withApollo HOC
* that provides the apolloContext
* to a next.js Page or AppTree.
* @param {Object} withApolloOptions
* @param {Boolean} [withApolloOptions.ssr=false]
* @returns {(PageComponent: ReactNode) => ReactNode}
*/
export const createWithApollo = (ac: any) => {
return ({ ssr = false } = {}) => (PageComponent: any) => {
const WithApollo = ({ apolloClient, apolloState, ...pageProps }: any) => {
let client;
if (apolloClient) {
// Happens on: getDataFromTree & next.js ssr
client = apolloClient;
} else {
// Happens on: next.js csr
client = initApolloClient(ac, apolloState, undefined);
}
return (
<ApolloProvider client={client}>
<PageComponent {...pageProps} />
</ApolloProvider>
);
};
// Set the correct displayName in development
if (process.env.NODE_ENV !== "production") {
const displayName =
PageComponent.displayName || PageComponent.name || "Component";
WithApollo.displayName = `withApollo(${displayName})`;
}
if (ssr || PageComponent.getInitialProps) {
WithApollo.getInitialProps = async (ctx: any) => {
const inAppContext = Boolean(ctx.ctx);
const { apolloClient } = initOnContext(ac, ctx);
// Run wrapped getInitialProps methods
let pageProps = {};
if (PageComponent.getInitialProps) {
pageProps = await PageComponent.getInitialProps(ctx);
} else if (inAppContext) {
pageProps = await App.getInitialProps(ctx);
}
// Only on the server:
if (typeof window === "undefined") {
const { AppTree } = ctx;
// When redirecting, the response is finished.
// No point in continuing to render
if (ctx.res && ctx.res.finished) {
return pageProps;
}
// Only if dataFromTree is enabled
if (ssr && AppTree) {
try {
// Import `@apollo/react-ssr` dynamically.
// We don't want to have this in our client bundle.
const { getDataFromTree } = await import(
"@apollo/client/react/ssr"
);
// Since AppComponents and PageComponents have different context types
// we need to modify their props a little.
let props;
if (inAppContext) {
props = { ...pageProps, apolloClient };
} else {
props = { pageProps: { ...pageProps, apolloClient } };
}
// Take the Next.js AppTree, determine which queries are needed to render,
// and fetch them. This method can be pretty slow since it renders
// your entire AppTree once for every query. Check out apollo fragments
// if you want to reduce the number of rerenders.
// https://www.apollographql.com/docs/react/data/fragments/
await getDataFromTree(<AppTree {...props} />);
} catch (error) {
// Prevent Apollo Client GraphQL errors from crashing SSR.
// Handle them in components via the data.error prop:
// https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-query-data-error
console.error("Error while running `getDataFromTree`", error);
}
// getDataFromTree does not call componentWillUnmount
// head side effect therefore need to be cleared manually
Head.rewind();
}
}
return {
...pageProps,
// Extract query data from the Apollo store
apolloState: apolloClient.cache.extract(),
// Provide the client for ssr. As soon as this payload
// gets JSON.stringified it will remove itself.
apolloClient: ctx.apolloClient,
};
};
}
return WithApollo;
};
};
function createApolloClient(apolloClient: any, initialState: any, ctx: any) {
// The `ctx` (NextPageContext) will only be present on the server.
// use it to extract auth headers (ctx.req) or similar.
apolloClient.ssrMode = Boolean(ctx);
apolloClient.cache.restore(initialState);
return apolloClient;
}
import Router from "next/router";
import { AppProps } from "next/app";
function MyApp({ Component, pageProps }: AppProps) {
return (
<Component {...pageProps} />
);
}
export default MyApp;
import React from "react";
import { withApollo } from "../apollo/withApollo";
const Index = () => <div></div>;
export default withApollo({ ssr: true })(Index);
这不是我想要的,@RomanHrybinchuk您的代码在服务器端和客户端都运行。如果您希望该查询只在服务器端运行,则必须使用getServerSideProps方法编写该查询。@RomanHrybinchuk是否修复了它?是的@SandroMennel,我可以在组件中附加您拥有的解决方案useQuery
,您的期望是什么?如果希望请求发生在服务器上,我们需要将其移动到getServerSideProps
。