通过SSR在getInitialProps中访问apolloClient

时间:2019-11-17 14:22:48

标签: apollo next.js react-apollo apollo-client

我希望在页面加载之前通过SSR获得信息。我一直在关注此示例https://github.com/zeit/next.js/tree/canary/examples/with-apollo-auth/pages,但是注意到apolloClient在getInitialProps中不存在。

我的withAuth.js

import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { createHttpLink } from 'apollo-link-http';
import { setContext } from 'apollo-link-context';
import { ApolloProvider } from 'react-apollo';
import PropTypes from 'prop-types';
import Head from 'next/head';
import Cookies from 'js-cookie';
import fetch from 'isomorphic-unfetch';

export const withApollo = (PageComponent, { ssr = true } = {}) => {
    const WithApollo = ({ apolloClient, apolloState, ...pageProps }) => {
        const client = apolloClient || initApolloClient(apolloState, { getToken });
        return (
            <ApolloProvider client={client}>
                <PageComponent {...pageProps} />
            </ApolloProvider>
        );
    };

    if (process.env.NODE_ENV !== 'production') {
        // Find correct display name
        const displayName = PageComponent.displayName || PageComponent.name || 'Component';

        // Warn if old way of installing apollo is used
        if (displayName === 'App') {
            console.warn('This withApollo HOC only works with PageComponents.');
        }

        // Set correct display name for devtools
        WithApollo.displayName = `withApollo(${displayName})`;

        // Add some prop types
        WithApollo.propTypes = {
            // Used for getDataFromTree rendering
            apolloClient: PropTypes.object,
            // Used for client/server rendering
            apolloState: PropTypes.object
        };
    }

    if (ssr || PageComponent.getInitialProps) {
        WithApollo.getInitialProps = async (ctx) => {
            const { AppTree } = ctx;
            console.log(AppTree);

            // Run all GraphQL queries in the component tree
            // and extract the resulting data
            const apolloClient = (ctx.apolloClient = initApolloClient(
                {},
                {
                    getToken: () => getToken(ctx.req)
                }
            ));

            const pageProps = PageComponent.getInitialProps ? await PageComponent.getInitialProps(ctx) : {};

            // Only on the server
            if (typeof window === 'undefined') {
                // When redirecting, the response is finished.
                // No point in continuing to render
                if (ctx.res && ctx.res.finished) {
                    return {};
                }

                if (ssr) {
                    try {
                        // Run all GraphQL queries
                        console.log('trying');
                        const { getDataFromTree } = await import('@apollo/react-ssr');
                        await getDataFromTree(
                            <AppTree
                                pageProps={{
                                    ...pageProps,
                                    apolloClient
                                }}
                            />
                        );
                    } 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();
            }

            // Extract query data from the Apollo store
            const apolloState = apolloClient.cache.extract();

            return {
                ...pageProps,
                apolloState
            };
        };
    }

    return WithApollo;
};

let apolloClient = null;

/**
 * Always creates a new apollo client on the server
 * Creates or reuses apollo client in the browser.
 */
const initApolloClient = (...args) => {
    // 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(...args);
    }

    // Reuse client on the client-side
    if (!apolloClient) {
        apolloClient = createApolloClient(...args);
    }

    return apolloClient;
};

const createApolloClient = (initialState = {}, { getToken }) => {
    let fetchOptions = {};
    const HTTP_ENDPOINT = 'http://localhost:4000/api';

    const httpLink = createHttpLink({
        uri: HTTP_ENDPOINT,
        credentials: 'same-origin',
        fetch,
        fetchOptions
    });

    const authLink = setContext((request, { headers }) => {
        const token = getToken();
        return {
            headers: {
                ...headers,
                authorization: token ? `Bearer ${token}` : ''
            }
        };
    });

    return new ApolloClient({
        ssrMode: typeof window === 'undefined', // Disables forceFetch on the server (so queries are only run once)
        link: authLink.concat(httpLink),
        cache: new InMemoryCache().restore(initialState)
    });
};

const getToken = () => {
    return Cookies.get('token');
};

我在_app.js文件中将其用作HOC,并尝试访问我的Signin组件中的apolloClient,希望进行检查是否有人登录,以便对其进行重定向(也可以想知道如何使导航栏变得动态)

谢谢您的帮助

2 个答案:

答案 0 :(得分:1)

尝试以下代码,现在您应该可以在getInitialProps中访问apolloClient。

const apolloClient = (ctx.ctx.apolloClient = initApolloClient({}, { 
      getToken: () => getToken(ctx.req)}));

答案 1 :(得分:0)

我认为您只是错过了一件事情,即当SSR为apolloClient时返回PagePropsApolloCache的同时返回true

// Extract query data from the Apollo store
const apolloState = apolloClient.cache.extract();

return {
    ...pageProps,
    apolloState,
    // To get access to client while in SSR
    apolloClient
};