Reputation: 1060
I'm building a NextJS app using headless WordPress with GraphQL. It's not clear from the documentation where I should be calling the query to create the site navigation. https://github.com/lfades/next.js/tree/examples/cms-wordpress/examples/cms-wordpress
The navigation is controlled dynamically by WordPress Menus (Appearance > Menus
) on the backend and I can successfully access these menuItems
via GraphQL without any issue on the index.js
and posts/[slug].js
page templates in Next JS.
// index.js
export default function Index({ primaryMenu = [] }) {
return (
<Layout>
<Header>
{primaryMenu.map((item, index) => {
return (<a href={item.url}>{item.label}</a>)
)}
</Header>
</Layout>
);
}
export async function getStaticProps() {
const primaryMenu = await getPrimaryMenu(); // Get menu via GraphQL
return {
props: { primaryMenu },
};
}
The issue I'm having with this is I am repeating the getStaticProps
function on each template and I should be able to use some sort of global query for this, either in the <header/>
component itself or another method. I'm unable to find documentation on how to do this and it doesn't work in components.
Any guidance (or examples) on where a global query such as a dynamic Navigation query would live in a NextJS app is appreciated.
Upvotes: 6
Views: 4350
Reputation: 1846
I battled this for a while (for JD site) with redux and wp rest, but I think theory should be the same for gql + apollo client.
You need to override Next App _app with a custom class that extends App.
And you might need to inject an instance of apollo client into AppContext using a HOC. I used this wrapper for Redux. Would need to be modelled after that.
Edit: (Looks like someone has made it already)
// export default withRedux(makeStore)(MyApp);
export default withApollo(apolloClient)(MyApp); ???
Then in your App getInitialProps, you can make query to get menu. By default apollo client query will grab cached value if it's in the cache store already I believe.
static async getInitialProps(appContext) {
const { isServer, pathname, apollo? } = appContext.ctx;
// do menu query
const menu = apollo.query???
// Redux version
// const state = store.getState();
// let main_menu = state.menu;
// if (!state.menu) {
// const menu = await apiService().getMenu("main");
// main_menu = menu;
// store.dispatch({ type: "SET_MENU", payload: menu });
// }
...
// call the page's `getInitialProps` and fills `appProps.pageProps`
const initialProps = await App.getInitialProps(appContext);
const appProps: any = {
...initialProps,
menu: main_menu
};
return appProps;
}
Now menu is in the page props of the App Component, which can be passed down.
Or you can use apollo client to make the query again in a child component. So when you make the query again, in header or whatever, it will take the cached response provided it's the same query.
I made an endpoint for menus that included the template name + post slug along with the menu items and mapped the wp templates to next routes.
const menu = useSelector((state: any) => state.menu);
const menuItems = menu.map((item: any) => {
const path = getTemplatePath(item.template);
return (
<Link key={item.slug} href={`/${path}`} as={`/${item.slug}`} scroll={false}>
<a>{item.title}</a>
</Link>
);
});
Upvotes: 0
Reputation: 76
There are a couple of ways you can do it:
You can menuItems query with useQuery() from @apollo/client inside the Layout component so that its available to all pages which are wrapped inside the Layout. However the problem with this is that, there will be a load time and the data won't be prefetched and readily available like we can do with getServerSideProps() ( at page level ). Because this will be at component level.
import { useQuery } from "@apollo/client";
export default function Layout () {
const { loading, data } = useQuery( GET_MENU_QUERY ) return {...} }
You can use swr that uses caching strategy. There is blog that explains how to use it
Upvotes: 1