Gatsby: How context in createPage really works?

Created on 23 Mar 2019  路  6Comments  路  Source: gatsbyjs/gatsby

Summary

Context in createPage is not provided. I have no idea how proper pass values to the context. Additionally onCreatePage log no value into console if needed -> It logs only for files in folder src/pages. Do you have any solution?

Relevant information

My code:

const _ = require(`lodash`);
const path = require(`path`);
const slash = require(`slash`);
const axios = require(`axios`);
const crypto = require(`crypto`);

const API_URL = 'https://b-abstract.com/api/wp-json';
const DEFAULT_LANGUAGE = 'en';
let requestCounter = 0;

const pageQuery = `{
  allPages {
    edges {
      node {
        id
        slug
        language
        index
      }
    }
  }
}`;

const SLUGS = ['/home', '/about', '/portfolio', '/contact'];

module.exports.sourceNodes = async ({ actions }) => {
  const { createNode } = actions;
  const fetchMenu = language => axios.get(`${API_URL}/mymenu/v3/menu/${language}`);
  const fetchLanguages = () => axios.get(`${API_URL}/v3/languages`);
  const fetchPage = id => axios.get(`${API_URL}/acf/v3/pages/${id}`);
  const fetchStrings = () => axios.get(`${API_URL}/v3/strings/`);

  const responseLanguages = await fetchLanguages();

  return new Promise(resolve => {
    responseLanguages.data.map(async (language, i) => {
      const responseMenu = await fetchMenu(language);
      responseMenu.data.acf.items.map(async (page, j) => {
        const responsePage = await fetchPage(page.object_id);

        const pageNode = {
          id: `${language}-${j}`,
          language,
          index: j,
          data: {
            ...responsePage.data.acf
          },
          slug: `${SLUGS[j]}`,
          internal: {
            type: `Pages`
          }
        };

        const contentDigest = crypto
          .createHash(`md5`)
          .update(JSON.stringify(pageNode))
          .digest(`hex`);

        pageNode.internal.contentDigest = contentDigest;

        createNode(pageNode);

        requestCounter++;
        if (responseLanguages.data.length * responseMenu.data.acf.items.length === requestCounter) {
          resolve();
        }
      });
    });
  });
};

module.exports.createPages = ({ graphql, actions }) => {
  const { createPage } = actions;

  return new Promise((resolve, reject) => {
    graphql(pageQuery).then(result => {
      console.log(result.data.allPages.edges);
      if (result.errors) {
        reject(result.errors);
      }

      const homeId = 'en-0';

      _.each(result.data.allPages.edges, (edge, index) => {
        const languagePath =
          edge.node.language !== DEFAULT_LANGUAGE ? `/${edge.node.language}` : '';
        const pageTemplate = path.resolve(`./src/templates${edge.node.slug}.jsx`);
        const pathPage = `${languagePath}${edge.node.slug}`;
        createPage({
          path: pathPage,
          component: slash(pageTemplate),
          context: {
            homeId,
          }
        });
      });

      resolve();
    });
  });
};
stale? question or discussion

Most helpful comment

@Brzonka see this for detailed example of Gatsby and the createPages api.
A couple of things

  1. One thing caught my eye, while reading your code.
const fetchMenu = language => axios.get(`${API_URL}/mymenu/v3/menu/${language}`);
  const fetchLanguages = () => axios.get(`${API_URL}/v3/languages`);
  const fetchPage = id => axios.get(`${API_URL}/acf/v3/pages/${id}`);
  const fetchStrings = () => axios.get(`${API_URL}/v3/strings/`);

  const responseLanguages = await fetchLanguages();

You need to add the await before each axios.get() and also with that you're making 2 calls to the same endpoint, You fetch the languages here const fetchLanguages = () => axios.get(${API_URL}/v3/languages); and then again here const responseLanguages = await fetchLanguages();

  1. Change the loop for the following:
const homeId = 'en-0';

      _.each(result.data.allPages.edges, (edge, index) => {
        const languagePath =
          edge.node.language !== DEFAULT_LANGUAGE ? `/${edge.node.language}` : '';
        const pageTemplate = path.resolve(`./src/templates${edge.node.slug}.jsx`);
        const pathPage = `${languagePath}${edge.node.slug}`;
        createPage({
          path: pathPage,
          component: slash(pageTemplate),
          context: {
             id_home:homeId,
          }
        });
      });

On your template component under templates,

import React from 'react'
import Layout from '../components/layout'
import SEO from '../components/seo'
import {Link} from 'gatsby'

const MyTemplate = props => {
  const {pageContext} = props
  const {id_home} = pageContext
  console.log('====================================');
    console.log(This is a item injected from pageContext:${id_home}``);
    console.log('====================================');
  return (
    <h1>This is the value passed through context {id_home}</h1>
  )
}
export default MyTemplate

Issue gatsby develop, openhttp://localhost:8000/your-page`, open the dev tools for your browser of choice and you should see the console.log output there.

Feel free to provide feedback to see if the issue is solved. If not, can you please share a reproducible example we can have a look at?

All 6 comments

What do you mean "Context in createPage is not provided"? In your code example, I can see you're sending homeId as context to the slash(pageTemplate) component.

The relevant doc is here: https://www.gatsbyjs.org/docs/creating-and-modifying-pages/

@Brzonka see this for detailed example of Gatsby and the createPages api.
A couple of things

  1. One thing caught my eye, while reading your code.
const fetchMenu = language => axios.get(`${API_URL}/mymenu/v3/menu/${language}`);
  const fetchLanguages = () => axios.get(`${API_URL}/v3/languages`);
  const fetchPage = id => axios.get(`${API_URL}/acf/v3/pages/${id}`);
  const fetchStrings = () => axios.get(`${API_URL}/v3/strings/`);

  const responseLanguages = await fetchLanguages();

You need to add the await before each axios.get() and also with that you're making 2 calls to the same endpoint, You fetch the languages here const fetchLanguages = () => axios.get(${API_URL}/v3/languages); and then again here const responseLanguages = await fetchLanguages();

  1. Change the loop for the following:
const homeId = 'en-0';

      _.each(result.data.allPages.edges, (edge, index) => {
        const languagePath =
          edge.node.language !== DEFAULT_LANGUAGE ? `/${edge.node.language}` : '';
        const pageTemplate = path.resolve(`./src/templates${edge.node.slug}.jsx`);
        const pathPage = `${languagePath}${edge.node.slug}`;
        createPage({
          path: pathPage,
          component: slash(pageTemplate),
          context: {
             id_home:homeId,
          }
        });
      });

On your template component under templates,

import React from 'react'
import Layout from '../components/layout'
import SEO from '../components/seo'
import {Link} from 'gatsby'

const MyTemplate = props => {
  const {pageContext} = props
  const {id_home} = pageContext
  console.log('====================================');
    console.log(This is a item injected from pageContext:${id_home}``);
    console.log('====================================');
  return (
    <h1>This is the value passed through context {id_home}</h1>
  )
}
export default MyTemplate

Issue gatsby develop, openhttp://localhost:8000/your-page`, open the dev tools for your browser of choice and you should see the console.log output there.

Feel free to provide feedback to see if the issue is solved. If not, can you please share a reproducible example we can have a look at?

Thanks @jonniebigodes for reply! You are right, context is provided there. But what about using variables in StaticQuery like:

query homePage($homeId: String) {
        content: pages(id: { eq: $homeId }) {
          data {
            slider {
              title
              title_on_hover
              letter
              description
            }
          }
        }
      }

I have a component, let say Layout, which is wrapPageElement and wrap all of my pages and I would like to use above StaticQuery inside, but I can not, because $homeId is undefined there. Do you know reason maybe?

@Brzonka to the best of my knowledge, as of the present version/state of Gatsby, StaticQuery does not allow arguments/variables. there's some work being done in that "department"(pardon the bad pun), but for what you want you would have to transform your StaticQuery into what known in the Gatsby ecosystem as a pagequery. More on that here. And a little insight on StaticQuery

Hiya!

This issue has gone quiet. Spooky quiet. 馃懟

We get a lot of issues, so we currently close issues after 30 days of inactivity. It鈥檚 been at least 20 days since the last update here.

If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not stale" to keep this issue open!

Thanks for being a part of the Gatsby community! 馃挭馃挏

Hey again!

It鈥檚 been 30 days since anything happened on this issue, so our friendly neighborhood robot (that鈥檚 me!) is going to close it.

Please keep in mind that I鈥檓 only a robot, so if I鈥檝e closed this issue in error, I鈥檓 HUMAN_EMOTION_SORRY. Please feel free to reopen this issue or create a new one if you need anything else.

Thanks again for being part of the Gatsby community!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

theduke picture theduke  路  3Comments

mikestopcontinues picture mikestopcontinues  路  3Comments

Oppenheimer1 picture Oppenheimer1  路  3Comments

kalinchernev picture kalinchernev  路  3Comments

ghost picture ghost  路  3Comments