getStaticProps returns an empty object - reactjs

I'm using nextJS V9.5.5 with wp-graphql and apolloClient to get data from WordPress. Everything works fine, but when I try to return context (in the purpose of getting query) from getStaticProps() like it's described in docs, it returns an empty object.
Custom App:
import React from "react";
import getConfig from "next/config";
import LayoutOuter from "../components/LayoutOuter";
import "bootstrap/dist/css/bootstrap.css";
import { ApolloProvider } from "#apollo/client";
import { useApollo } from "../lib/apolloClient";
import { initializeApollo } from "../lib/apolloClient";
import { gql } from "#apollo/client";
const { serverRuntimeConfig, publicRuntimeConfig } = getConfig();
const { DOMAIN } = publicRuntimeConfig;
function CustomApp({ pageProps, Component, props }) {
const apolloClient = useApollo(pageProps.initialApolloState);
return (
<ApolloProvider client={apolloClient}>
{console.log("_app", props)}
<LayoutOuter>
<Component {...pageProps} />
</LayoutOuter>
</ApolloProvider>
);
}
CustomApp.getInitialProps = async (ctx) => {
const apolloClient = initializeApollo();
await apolloClient.query({
query: gql`
{
// my graphql query here
}
`,
});
return {
props: {
initialApolloState: apolloClient.cache.extract(),
ctx: JSON.stringify(ctx),
},
};
};
export default CustomApp;
One of the page:
import React, { Component, useEffect, useState } from "react";
import getConfig from "next/config";
import { NextSeo } from "next-seo";
const { serverRuntimeConfig, publicRuntimeConfig } = getConfig();
const { DOMAIN, SITENAME } = publicRuntimeConfig;
import { initializeApollo } from "../lib/apolloClient";
import { gql } from "#apollo/client";
import "./services.module.scss";
const Home = (props) => {
let currentPage = Object.values(props.initialApolloState.ROOT_QUERY)[1];
const {
title,
metadesc,
metaRobotsNoindex,
metaRobotsNofollow,
metaRobotsAdv,
opengraphTitle,
opengraphDescription,
opengraphImage,
twitterTitle,
twitterDescription,
twitterImage,
} = currentPage.seo;
return (
<>
{console.log("project", props)}
<NextSeo
noindex={metaRobotsNoindex}
nofollow={metaRobotsNofollow}
title={title != "" ? title : `${props.data.pagetitle} - ${SITENAME}`}
description={metadesc}
canonical={DOMAIN}
openGraph={{
url: DOMAIN,
title:
opengraphTitle != ""
? opengraphTitle
: `${props.data.pagetitle} - Garrison Collection`,
description: opengraphDescription,
images: [
{
url: opengraphImage,
width: 800,
height: 600,
alt: { SITENAME },
},
],
site_name: { SITENAME },
}}
/>
<p>works</p>
</>
);
};
export async function getStaticProps(context) {
const apolloClient = initializeApollo();
await apolloClient.query({
query: gql`
{
project(id: "ca-souls", idType: SLUG) {
seo {
canonical
metaDesc
metaKeywords
metaRobotsNofollow
metaRobotsNoindex
opengraphAuthor
opengraphDescription
opengraphModifiedTime
opengraphPublishedTime
opengraphPublisher
opengraphSiteName
opengraphTitle
opengraphType
opengraphUrl
title
twitterDescription
twitterTitle
}
}
}
`,
});
return {
props: {
initialApolloState: apolloClient.cache.extract(),
context: JSON.stringify(context) || null,
},
revalidate: 1,
};
}
export default Home;
Here is the log result:
How could I get context.query?

The context parameter includes previewData contains the preview data set by setPreviewData. This means including function, therefore, unable to serealize. Take values out from context.params.

Related

How to get client from wrapper in next.js getServerSideProps()?

I'm trying to use next.js with apollo graphql for server-side rendering. I know that to do that i need to run the necessary queries inside getServerSideProps(), which then will pass the props into the main component, where i will be able to render the results.
I created a provider to make sure all components in the tree get the same client object.
import withApollo from "next-with-apollo";
import { ApolloClient, InMemoryCache } from "#apollo/client";
import { ApolloProvider } from "#apollo/react-hooks";
export default withApollo(
() => {
return new ApolloClient({
ssrMode: true,
uri: "https://my.api/graphql",
cache: new InMemoryCache()
});
},
{
render: ({ Page, props }) => {
return (
<ApolloProvider client={props.apollo}>
<Page {...props} />
</ApolloProvider>
);
}
}
);
but how can i get this client inside the getServerSideProps() function if it's not being wrapped by withApollo()?
import gql from "graphql-tag";
import { useQuery } from "#apollo/react-hooks";
import { ApolloClient } from "#apollo/client";
import withApollo from "next-with-apollo";
const MY_QUERY = gql`
query MyQuery {
myQuery {
name
}
}
`;
function MyComponent(props) {
return (
<div className="landing-section__topcontainer ph-lg-8 ph-3">
<div className="overflow-list-container">
<div className="landing-horizontal-list">
{props.res.map(q => {
return (
<div className="tag-tile__title">{q.name}</div>
);
})}
</div>
</div>
</div>
);
}
export async function getServerSideProps() {
// Fetch data from external API
const apolloClient = getApolloClient();
const { data } = await apolloClient.query({
query: MY_QUERY
});
const res = data.myQuery;
return { props: { res } };
}
export default withApollo(MyComponent);

Next JS code inside getInitialProps not executes after page reload

I'm integrating NextJS into my React app. I face a problem, on page reload or opening direct link(ex. somehostname.com/clients) my getInitialProps not executes, but if I open this page using <Link> from next/link it works well. I don't really understand why it happens and how to fix it. I have already came throught similar questions, but didn't find any solution which could be suitable for me.
Clients page code:
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { ClientsTable } from '../../src/components/ui/tables/client-table';
import AddIcon from '#material-ui/icons/Add';
import Fab from '#material-ui/core/Fab';
import { AddClientModal } from '../../src/components/ui/modals/add-client-modal';
import CircularProgress from '#material-ui/core/CircularProgress';
import { Alert } from '../../src/components/ui/alert';
import { Color } from '#material-ui/lab/Alert';
import { AppState } from '../../src/store/types';
import { thunkAddClient, thunkGetClients } from '../../src/store/thunks/clients';
import { SnackbarOrigin } from '#material-ui/core';
import { IClientsState } from '../../src/store/reducers/clients';
import { NextPage } from 'next';
import { ReduxNextPageContext } from '../index';
import { PageLayout } from '../../src/components/ui/page-layout';
const Clients: NextPage = () => {
const [addClientModalOpened, setAddClientModalOpened] = useState<boolean>(false);
const [alertType, setAlertType] = useState<Color>('error');
const [showAlert, setAlertShow] = useState<boolean>(false);
const alertOrigin: SnackbarOrigin = { vertical: 'top', horizontal: 'center' };
const dispatch = useDispatch();
const { clients, isLoading, hasError, message, success } = useSelector<AppState, IClientsState>(state => state.clients);
useEffect(() => {
if (success) {
handleAddModalClose();
}
}, [success]);
useEffect(() => {
checkAlert();
}, [hasError, success, isLoading]);
function handleAddModalClose(): void {
setAddClientModalOpened(false);
}
function handleAddClient(newClientName: string): void {
dispatch(thunkAddClient(newClientName));
}
function checkAlert() {
if (!isLoading && hasError) {
setAlertType('error');
setAlertShow(true);
} else if (!isLoading && success) {
setAlertType('success');
setAlertShow(true);
} else {
setAlertShow(false);
}
}
return (
<PageLayout>
<div className='clients'>
<h1>Clients</h1>
<div className='clients__add'>
<div className='clients__add-text'>
Add client
</div>
<Fab color='primary' aria-label='add' size='medium' onClick={() => setAddClientModalOpened(true)}>
<AddIcon/>
</Fab>
<AddClientModal
opened={addClientModalOpened}
handleClose={handleAddModalClose}
handleAddClient={handleAddClient}
error={message}
/>
</div>
<Alert
open={showAlert}
message={message}
type={alertType}
origin={alertOrigin}
autoHideDuration={success ? 2500 : null}
/>
{isLoading && <CircularProgress/>}
{!isLoading && <ClientsTable clients={clients}/>}
</div>
</PageLayout>
);
};
Clients.getInitialProps = async ({ store }: ReduxNextPageContext) => {
await store.dispatch(thunkGetClients());
return {};
};
export default Clients;
thunkGetClients()
export function thunkGetClients(): AppThunk {
return async function(dispatch) {
const reqPayload: IFetchParams = {
method: 'GET',
url: '/clients'
};
try {
dispatch(requestAction());
const { clients } = await fetchData(reqPayload);
console.log(clients);
dispatch(getClientsSuccessAction(clients));
} catch (error) {
dispatch(requestFailedAction(error.message));
}
};
}
_app.tsx code
import React from 'react';
import App, { AppContext, AppInitialProps } from 'next/app';
import withRedux from 'next-redux-wrapper';
import { Provider } from 'react-redux';
import { makeStore } from '../../src/store';
import { Store } from 'redux';
import '../../src/sass/app.scss';
import { ThunkDispatch } from 'redux-thunk';
export interface AppStore extends Store {
dispatch: ThunkDispatch<any, any, any>;
}
export interface MyAppProps extends AppInitialProps {
store: AppStore;
}
export default withRedux(makeStore)(
class MyApp extends App<MyAppProps> {
static async getInitialProps({
Component,
ctx
}: AppContext): Promise<AppInitialProps> {
const pageProps = Component.getInitialProps
? await Component.getInitialProps(ctx)
: {};
return { pageProps };
}
render() {
const { Component, pageProps, store } = this.props;
return (
<>
<Provider store={store}>
<Component {...pageProps} />
</Provider>
</>
);
}
}
);
Looking for your advices and help. Unfortunately, I couldn't find solution by myself.
This is the way Next.js works, it runs getInitialProps on first page load (reload or external link) in the server, and rest of pages that where navigated to with Link it will run this method on client.
The reason for this is to allow Next.js sites to have "native" SEO version.

Using `react-apollo-hooks` and `useSubscription` hook

I'm building a simple todo app using React, Apollo and react-apollo-hooks for hooks support, but the useSubscription hook doesnt fire.
I know the actual backend stuff works, because I have a graphiql app set up, and whenever I save a todo, the todoCreated event shows up in graphiql. I also know that the websocket-setup is working properly, because the queries & mutations are going through the websocket. I'm using Elixir, Phoenix, Absinthe, by the way, for the backend stuff.
Here's the Todo-app component:
import React, { useState } from 'react';
import gql from 'graphql-tag';
import { useQuery, useMutation, useSubscription } from 'react-apollo-hooks';
import styles from 'styles.css';
const TODO_FRAGMENT = gql`
fragment TodoFields on Todo {
id
description
}
`;
const GET_TODOS = gql`
{
todos {
...TodoFields
}
}
${TODO_FRAGMENT}
`;
const SAVE_TODO = gql`
mutation createTodo($description: String!) {
createTodo(description: $description) {
...TodoFields
}
}
${TODO_FRAGMENT}
`;
const DELETE_TODO = gql`
mutation deleteTodo($id: ID!) {
deleteTodo(id: $id) {
id
}
}
`;
const NEW_TODO_SUBSCRIPTION = gql`
subscription {
todoCreated {
...TodoFields
}
}
${TODO_FRAGMENT}
`;
const Todos = () => {
const [inputValue, setInputValue] = useState('');
const { data, error, loading } = useQuery(GET_TODOS);
const saveTodo = useMutation(SAVE_TODO, {
update: (proxy, mutationResult) => {
proxy.writeQuery({
query: GET_TODOS,
data: { todos: data.todos.concat([mutationResult.data.createTodo]) },
});
},
});
const deleteTodo = useMutation(DELETE_TODO, {
update: (proxy, mutationResult) => {
const id = mutationResult.data.deleteTodo.id
proxy.writeQuery({
query: GET_TODOS,
data: { todos: data.todos.filter(item => item.id !== id) },
});
},
});
const subData = useSubscription(NEW_TODO_SUBSCRIPTION);
console.log(subData);
if (loading) {
return <div>Loading...</div>;
};
if (error) {
return <div>Error! {error.message}</div>;
};
return (
<>
<h1>Todos</h1>
{data.todos.map((item) => (
<div key={item.id} className={styles.item}>
<button onClick={() => {
deleteTodo({
variables: {
id: item.id,
},
});
}}>Delete</button>
{' '}
{item.description}
</div>
))}
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
type="text"
/>
<button onClick={() => {
saveTodo({
variables: {
description: inputValue,
},
});
setInputValue('');
}}>Save</button>
</>
);
};
export default Todos;
And here's the root component:
import React from 'react';
import { ApolloProvider } from 'react-apollo';
import { ApolloProvider as ApolloHooksProvider } from 'react-apollo-hooks';
import Todos from 'components/Todos';
import apolloClient from 'config/apolloClient';
const App = () => (
<ApolloHooksProvider client={apolloClient}>
<Todos />
</ApolloHooksProvider>
);
export default App;
Anyone have a clue on what I seem to be doing wrong?
Sorry, I figured it out, it was a silly mistake on my part. The problem seems to have been with my apolloClient setup:
import { split } from 'apollo-link';
import { getMainDefinition } from 'apollo-utilities';
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
import { onError } from 'apollo-link-error';
import { ApolloLink } from 'apollo-link';
import absintheSocketLink from 'config/absintheSocketLink';
const apolloClient = new ApolloClient({
link: ApolloLink.from([
onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.map(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
),
);
if (networkError) console.log(`[Network error]: ${networkError}`);
}),
split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
new HttpLink({
uri: 'http://localhost:4000/api/graphql',
credentials: 'same-origin'
}),
absintheSocketLink,
),
]),
cache: new InMemoryCache()
});
export default apolloClient;
The error in the code above is the fact that the line
absintheSocketLink,
is in the wrong place. It should've been before the HttpLink.
Silly me.
I had the same issue my subscription was always sending null data and i had a silly mistake as well.

Could not find "client" in the context of ApolloConsumer

I am trying to implement react-apollo to store my local states, I can write to them using client.writeData() but when i try to access them, I keep getting errors
I tried following https://github.com/wesbos/Advanced-React/tree/master/finished-application/frontend. I tried implementing apollo-link-state as well but keeps throwing error.
withData.js
import withApollo from "next-with-apollo";
import { ApolloClient } from "apollo-client";
import { createHttpLink } from "apollo-link-http";
import { InMemoryCache } from "apollo-cache-inmemory";
const cache = new InMemoryCache();
function createClient({ headers }) {
return new ApolloClient({
cache,
link: new createHttpLink({
uri: "http://localhost:4000/",
request: operation => {
operation.setContext({
fetchOptions: {
credentials: "same-origin"
},
headers: {
"Content-Type": "application/json"
}
});
}
}),
clientState: {
resolvers: {
Query: {
getLocalData: (_, { text }, { cache }) => {
const query = gql`
query getLocalData {
loading #client
}
`;
const previous = cache.readQuery({ query });
return previous.loading;
}
},
Muatation: {
toggleLoading: (_, { text }, { cache }) => {
const query = gql`
query getLocalData {
loading #client
}
`;
var previous = cache.readQuery({ query });
cache.writeData({ data: { loading: !previous.loading } });
return null;
}
}
},
defaults: {
loading: true,
},
typeDefs: {}
}
});
}
export default withApollo(createClient);
_app.js
import App, { Container } from 'next/app';
import Page from '../components/Page';
import { ApolloProvider } from 'react-apollo';
import withData from '../lib/withData';
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
let pageProps = {};
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
// this exposes the query to the user
pageProps.query = ctx.query;
return { pageProps };
}
render() {
const { Component, apollo, pageProps } = this.props;
return (
<Container>
<ApolloProvider client={apollo}>
<Page>
<Component {...pageProps} />
</Page>
</ApolloProvider>
</Container>
);
}
}
export default withData(MyApp);
I expect to run efficiently
but the actual output is: Could not find "client" in the context of ApolloConsumer. Wrap the root component in an

Mocking apollo link state

I am trying to mock a query #client and I am not getting.
I mocked the query from graphql server correctly and it's working.
import React from 'react';
import renderer from 'react-test-renderer';
import wait from 'waait';
import ExchangeRates from './ExchangeRates';
import { MockedProvider } from 'react-apollo/test-utils';
import { sucessMockrates, errorMockrates } from '../../mocks/exchangeRatesMock';
describe('ExchangeRates', () => {
it('should render rate', async () => {
const component = renderer.create(
<MockedProvider mocks={[sucessMockrates]} addTypename={false}>
<ExchangeRates />
</MockedProvider>
);
await wait(0);
const p = component.root.findByType('p');
expect(p.children).toContain('AED: 3.67');
});
it('should render loading state initially', () => {
const component = renderer.create(
<MockedProvider mocks={[]}>
<ExchangeRates />
</MockedProvider>
);
const tree = component.toJSON();
expect(tree.children).toContain('Loading...');
});
it('should show error UI', async () => {
const component = renderer.create(
<MockedProvider mocks={[errorMockrates]} addTypename={false}>
<ExchangeRates />
</MockedProvider>
);
await wait(0);
const tree = component.toJSON();
expect(tree.children).toContain('Error!');
});
});
I am using the graphql server link from apollo tutorial
But when I tried to test the apollo query with local state I got an error.
My query:
import gql from 'graphql-tag';
export default gql`
query {
allocations #client {
list
}
}
`;
and my apollo client setup:
const cache = new InMemoryCache();
const defaultState = {
allocations: {
__typename: 'Allocations',
list: [],
},
};
const listQuery = gql`
query getAllocations {
allocations #client {
list
}
}
`;
const stateLink = withClientState({
cache,
defaults: defaultState,
resolvers: {
addAllocation: (
_,
{ userName },
{ cache }
) => {
const previousState = cache.readQuery({ query: listQuery });
const { list } = previousState.allocations;
const data = {
...previousState,
allocations: {
...previousState.allocations,
list: [
...list,
{
userName
},
],
},
};
cache.writeQuery({ query: listQuery, data });
return data.allocations;
},
},
},
});
const client = new ApolloClient({
link: ApolloLink.from([
stateLink,
new HttpLink({
uri: 'https://w5xlvm3vzz.lp.gql.zone/graphql',
}),
]),
cache,
});
My test with apollo local state:
import React from 'react';
import renderer from 'react-test-renderer';
import AllocationListPage from './AllocationListPage';
import { MockedProvider } from 'react-apollo/test-utils';
import { sucessMockAllocations } from '../../../mocks/allocationListMock';
describe('AllocationListPage', () => {
it('should render list of allocations', () => {
renderer.create(
<MockedProvider mocks={[sucessMockAllocations]} addTypename={false}>
<AllocationListPage />
</MockedProvider>
);
});
});
The error I got: TypeError:
Cannot destructure property list of 'undefined' or 'null'.
I need to mock the initial state of apollo local state, and I don't know how.
Thanks in advance.
I got setup my apollo link state with this component:
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { ApolloProvider } from 'react-apollo';
import { makeExecutableSchema, addMockFunctionsToSchema } from 'graphql-tools';
import { ApolloClient } from 'apollo-client';
import { stateLink, cache } from '../graphql/stateLink';
import { ApolloLink } from 'apollo-link';
import { SchemaLink } from 'apollo-link-schema';
const setupClient = mocks => {
const typeDefs = `
type Query {
test: String!
}
`;
const schema = makeExecutableSchema({ typeDefs });
addMockFunctionsToSchema({
schema,
mocks,
preserveResolvers: false,
});
return new ApolloClient({
cache,
link: ApolloLink.from([stateLink, new SchemaLink({ schema })]),
});
};
class ApolloLinkStateSetup extends PureComponent {
render() {
return (
<ApolloProvider client={setupClient(this.props.mocks)}>
{this.props.children}
</ApolloProvider>
);
}
}
ApolloLinkStateSetup.defaultProps = {
mocks: {},
};
ApolloLinkStateSetup.propTypes = {
children: PropTypes.object.isRequired,
mocks: PropTypes.object,
};
export default ApolloLinkStateSetup;
You can mock the graphql queries with makeExecutableSchema and addMockFunctionsToSchema from graphql-tools. This mock can be useful to create the front-end side without the back-end side.

Resources