vercel / next.js

The React Framework
https://nextjs.org
MIT License
125.06k stars 26.71k forks source link

Server Side State not getting hydrated #14441

Closed serendipity1004 closed 4 years ago

serendipity1004 commented 4 years ago

I am following the example on using apollo with NextJS.

However, I am unable to get the data retrieved in server to be hydrated in the client.

In the line below, if I print initialState it is always null; therefore, the cache is not populated to the client side apollo. https://github.com/vercel/next.js/blob/19f3306aaadc00be1ba6d3521dfdeca49d7a95fe/examples/api-routes-apollo-server-and-client/apollo/client.js#L32-L36

My client.tsx file looks like below

import fetch from "isomorphic-unfetch";

let apolloClient;

function createIsomorphLink() {
    let uri;

    if (typeof window == 'undefined') {
        uri = CONFIG.apolloServerSideUrl;
    }else{
        uri = CONFIG.apolloClientSideUrl;
    }

    return new HttpLink({
        fetch:fetch,
        uri: uri,
        credentials: 'same-origin',
    });
}

function createApolloClient() {
    return new ApolloClient({
        ssrMode: typeof window === 'undefined',
        link: createIsomorphLink(),
        cache: new InMemoryCache(),
    })
}

export function initializeApollo(initialState = null) {
    const _apolloClient = apolloClient ?? createApolloClient();

    // If your page has Next.js data fetching methods that use Apollo Client, the initial state
    // get hydrated here
    console.log('initial state');
    console.log(initialState);
    if (initialState) {
        _apolloClient.cache.restore(initialState);
    }
    // For SSG and SSR always create a new Apollo Client
    if (typeof window === 'undefined') return _apolloClient;
    // Create the Apollo Client once in the client
    if (!apolloClient) apolloClient = _apolloClient;

    return _apolloClient
}

export function useApollo(initialState) {
    const store = useMemo(() => initializeApollo(initialState), [initialState])
    return store
}

my index.tsx page looks like below

const GET_CERTIFICATES = gql`
    query certificates{
        certificates{
            id
            createdAt
            updatedAt
        }
    }
`;

const useStyles = makeStyles((theme: Theme) => createStyles({
    toolbar:{
        backgroundColor:'red',
    }
}));

const Blog = () => {
    const classes = useStyles();
    const {data, loading, error} = useQuery(GET_CERTIFICATES);
    console.log('data');
    console.log(error);
    console.log(data);

    return (
        <>
            <CssBaseline/>
            hello
            {
                data ? data.certificates.map(
                    (item) => (
                        <div>
                            {item.id}
                        </div>
                    )
                ) : null
            }
        </>
    )
};

export async function getServerSideProps() {
    const apolloClient = initializeApollo();

    await apolloClient.query({
        query:GET_CERTIFICATES,
    });

    return {
        props:{
            initializeApollo:apolloClient.cache.extract(),
        }
    }
}

export default Blog;

When I print apolloClient.cache.extract() I am getting a correct cache value; however, this is not passed on to initialState in the initializeApollo function.

I am also using Material UI so _document.tsx and _app.tsx are looking like below, respectively.

_document.tsx

const theme = responsiveFontSizes(createMuiTheme())

class MyDocument extends Document {
    render() {
        return (
            <Html>
                <Head>
                    <meta charSet="utf-8" />
                    <meta
                        name="viewport"
                        content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no"
                    />
                    <meta name="theme-color" content={theme.palette.primary.main} />
                    <link
                        rel="stylesheet"
                        href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Roboto+Slab:400,700|Material+Icons"
                    />
                    <style jsx global>
                        {`
              html,
              body {
                height: 100%;
                width: 100%;
              }
              *,
              *:after,
              *:before {
                box-sizing: border-box;
              }
              body {
                font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif;
                font-size: 1rem;
                margin: 0;
              }
            `}
                    </style>
                </Head>
                <body>
                <Main />
                <NextScript />
                </body>
            </Html>
        )
    }
}//

MyDocument.getInitialProps = async ctx => {
    // Render app and page and get the context of the page with collected side effects.
    const sheets = new ServerStyleSheets();
    const originalRenderPage = ctx.renderPage

    ctx.renderPage = () =>
        originalRenderPage({
            enhanceApp: App => props => sheets.collect(<App {...props} />)
        });

    const initialProps = await Document.getInitialProps(ctx);

    return {
        ...initialProps,
        // Styles fragment is rendered after the app and page rendering finish.
        styles: [
            <React.Fragment key="styles">
                {initialProps.styles}
                {sheets.getStyleElement()}
            </React.Fragment>
        ]
    }
};

export default MyDocument

_app.tsx

export default function App({ Component, pageProps }) {
    const apolloClient = useApollo(pageProps.initialApolloState);

    React.useEffect(() => {
        // Remove the server-side injected CSS.
        const jssStyles = document.querySelector('#jss-server-side');
        if (jssStyles) {
            jssStyles.parentElement.removeChild(jssStyles);
        }
    }, []);

    return (
        <ApolloProvider client={apolloClient}>
            <CssBaseline/>
            <Component {...pageProps} />
        </ApolloProvider>
    )
}

What should I do in order to have the SSR apollo cache populate the Client Side apollo cache?

serendipity1004 commented 4 years ago

this is a very stupid mistake on my part.

for those of you having same problem the key name at below two codes must be identical for obvious reasons

https://github.com/vercel/next.js/blob/19f3306aaadc00be1ba6d3521dfdeca49d7a95fe/examples/with-apollo/pages/index.js#L30

https://github.com/vercel/next.js/blob/19f3306aaadc00be1ba6d3521dfdeca49d7a95fe/examples/with-apollo/pages/_app.js#L5

balazsorban44 commented 2 years ago

This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.