question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Creating 2 layouts from same component with different context

See original GitHub issue

Hello,

I am developing a multilingual website and need to pass the current language to the layout. For this I used createLayout:

const path = require(`path`)
const slash = require(`slash`)

const MainLayout = path.resolve(`./src/templates/MainLayout.jsx`)
const homeTemplate = path.resolve(`./src/templates/Home.jsx`)
const languagePath = {
  'ar-EG': '/',
  'fr-FR': '/fr/',
}
Object.keys(languagePaths).forEach((locale) => {
    createLayout({
    component: slash(MainLayout),
    id: `main-layout-${locale}`,
    context: {
        locale,
    },
    })
})

// home pages
Object.entries(languagePaths).forEach(([locale, languagePath]) => {
    createPage({
    path: languagePath,
    component: slash(homeTemplate),
    context: {
        languagePath,
        locale,
    },
    layout: `main-layout-${locale}`
    })
})

The problem is that when I log props.layoutContext.locale inside my MainLayout, I always get fr-FR (on the arabic page and the french page)

While investigating, I could make it work when I duplicate my template/MainLayout file, and use one file for each language (this is very very ugly):

const MainLayout = path.resolve(`./src/templates/MainLayout.jsx`)
const MainLayout2 = path.resolve(`./src/templates/MainLayout2.jsx`)
// layout for each lang
Object.keys(languagePaths).forEach((locale, i) => {
    createLayout({
    component: slash(i === 1 ? MainLayout2 : MainLayout),
    id: `main-layout-${locale}`,
    context: {
        locale,
    },
    })
})

After that I tried to debug it in gatsby, cloned the repo, and configured redux-devtools.

While examining the store, I noticed that the layouts are created correctly with the good context for each layout, and that pages are also created correctly, with their corresponding layout id.

I am a bit stuck because redux-tools resets its history while building, so I cannot look at the actions creating the pages to try and debug more.

Can you give me any indication to how to investigate further ?

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:8 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
abumalickcommented, Jan 14, 2018

It is not nice to pass data from the page to the layout in my opinon, it is like passing data from a child to a parent when we can pass the data in the normal way.

I prefer creating a layout for each language. All pages of one language will use the same layout. Here is some code:

gatsby-node.js :

const _ = require(`lodash`)
const Promise = require(`bluebird`)
const path = require(`path`)
const slash = require(`slash`)

exports.createPages = ({graphql, boundActionCreators}) => {
  const {createLayout, createPage} = boundActionCreators
  return new Promise((resolve, reject) => {
    // We get the language path

    graphql(
      `
        {
          allContentfulWebsite(limit: 1000) {
            edges {
              node {
                languageName
                languagePath
                node_locale
              }
            }
          }
        }
      `,
    ).then((languagesRes) => {
      if (languagesRes.errors) {
        reject(languagesRes.errors)
      }

      const MainLayout = path.resolve(`./src/templates/MainLayout.jsx`)

      const homeTemplate = path.resolve(`./src/templates/Home.jsx`)

      // we store the list of languages with their path
      const languagePaths = {}
      // We need languages list on all pages
      // We prepare it here to be able to choose enabled languages from here
      const languages = []
      _.each(
        languagesRes.data.allContentfulWebsite.edges,
        ({node: {languageName, languagePath, node_locale}}) => {
          languagePaths[node_locale] = languagePath
          languages.push({
            languageName,
            languagePath,
          })
        },
      )

      // layout for each lang
      Object.keys(languagePaths).forEach((locale) => {
        createLayout({
          component: slash(MainLayout),
          id: `main-layout-${locale}`,
          context: {
            languages,
            locale,
          },
        })
      })

      // home pages
      Object.entries(languagePaths).forEach(([locale, languagePath]) => {
        createPage({
          path: languagePath,
          component: slash(homeTemplate),
          context: {
            languagePath,
            languages,
            locale,
          },
          layout: `main-layout-${locale}`,
        })
      })
      resolve()
    })
  })
}

MainLayout.jsx

import * as React from 'react'
import 'tachyons' // eslint-disable-line
import LanguagesNav from '../components/LanguagesNav'
import './styles.css'

const MainLayout = ({children, data, layoutContext, location}) => {
  const {languageChoiceLabel} = data.contentfulWebsite
  const {languages} = layoutContext
  const {pathname} = location
  return (
    <div>
      <LanguagesNav
        languageChoiceLabel={languageChoiceLabel}
        languages={languages}
        pathname={pathname}
      />
      {children()}
    </div>
  )
}

export default MainLayout

export const pageQuery = graphql`
  query mainWrapperQuery($locale: String!) {
    contentfulWebsite(node_locale: {eq: $locale}) {
      languageChoiceLabel
    }
  }
`

For the translated slugs I don’t know how you have to do it though.

0reactions
danjebscommented, Jan 17, 2018

Thanks @abumalick, I ended up using the English (base) slugs for the translated articles.

Seems it’s the way Gatsby is structured. In Jekyll you don’t specifically pass variables back up the layout, but the layout will execute one time for each page and so it can expect variables as if it were the page itself. In Gatsby it seems to render the template once, then repeat that for each page.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to use context in layout when we have two separate ...
I use two separate layouts in NextJS. When I call the context like this, I have access to the values in the children...
Read more >
Multiple Layouts in React app with React Router v5
This component is able to take an array of paths, which greatly facilitates the use of different layouts. We can create Route component...
Read more >
Basic Features: Layouts - Next.js
If you need multiple layouts, you can add a property getLayout to your page, allowing you to return a React component for the...
Read more >
Reuse layouts with <include> - Android Developers
Reuse layouts with <include>​​ To efficiently reuse complete layouts, you can use the <include> and <merge> tags to embed another layout inside ...
Read more >
How To Share State Across React Components with Context
React context is an interface for sharing information with other compone… ... Since you are building a small app with multiple components, ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found