bitcasual
bitcasual

Reputation: 83

Gatsby Link pass previous page's location.path to next page via Props

I have several generated pages with pagination listing all my blog posts, each blog post is generated from markdown using createPage(). Each generated /posts page hold 3 Previews of the posts and following pages gets a number added posts/2..etc

I want to pass the path of my current index page (which for instance can be /posts/3) to the Blog page I click on, so I can make a "Back" Link on each of them. I can pass data using the state attribute in the Link component, but I'm not sure passing the location.path from the list page with props is the proper way to go about since it doesn't work, it links to the actual current page.

I've omitted import-statements and unrelated code for readability.

gatsby-node.js

const { createPage } = actions
const blogPostTemplate = require.resolve(path.join(__dirname, `src`, `templates`, `blog-post-template.js`))
const blogListTemplate = require.resolve(path.join(__dirname, `src`, `templates`, `blog-list-template.js`))
const blogPosts = result.data.allMarkdownRemark.edges
// Create the list pages of the blog posts using the Awesome Pagination plugin
paginate({
  createPage, 
  items: blogPosts,
  itemsPerPage: 3,
  pathPrefix: `/posts`,
  component: blogListTemplate
})
  
// Create a page for each blog post
blogPosts.forEach(({ node }) => {
  createPage({
    path: node.frontmatter.slug,
    component: blogPostTemplate,
    context: {
      slug: node.frontmatter.slug,
    }
  })
})

blog-list-template

const BlogListTemplate = ({ data, pageContext, location }) => {
  const pathBack= location.pathname
  return (
    <Layout>
      {posts.map(({ node }) => (
        <Preview
          key={node.id}
          to={node.frontmatter.slug}
          from={pathBack}
          title={node.frontmatter.title}
          date={node.frontmatter.date}
          excerpt={node.excerpt}
        />
      ))}
    </Layout>
  ) 
}

preview.js

const Preview = ({ to, from, title, date, excerpt}) => {
  console.log("This is preview: path ", from)
  return(
    <div>
      <Link
      to={to}
      state={{ path: from}}
      >
        <h2>{title}</h2>
        <p> - {date} - </p>
      </Link>
      <p>{excerpt}</p>
    </div>
  )
}

blog-post-template.js

const BlogPostTemplate = ({ data, path }) => {
  return (
    <Layout>
      <div className="blogPost">
        <Link to={path}>Tillbaka</Link>
        <h1>{frontmatter.title}</h1>
        <div className="blog-post-content" dangerouslySetInnerHTML={{__html: html}} />
        <p>Publicerad: {frontmatter.date}</p>
      </div>
    </Layout>

Upvotes: 3

Views: 719

Answers (1)

Ferran Buireu
Ferran Buireu

Reputation: 29320

Your approach is perfectly valid (and in my opinion is the easiest and cleanest). You are missing the props destructuring to get the path. Your code should look like:

const BlogPostTemplate = ({ data, location }) => {
  return (
    <Layout>
      <div className="blogPost">
        <Link to={location.state.path}>Tillbaka</Link>
        <h1>{frontmatter.title}</h1>
        <div className="blog-post-content" dangerouslySetInnerHTML={{__html: html}} />
        <p>Publicerad: {frontmatter.date}</p>
      </div>
    </Layout>

I don't know if it's a typo, but frontmatter will become undefined, breaking your code in this snippet, I guess you are trying to do data.frontmatter.

Note that everything that is sent via <Link>'s state, needs to be retrieved as props.location.state, as you can see in the docs's example:

const PhotoFeedItem = ({ id }) => (
  <div>
    {/* (skip the feed item markup for brevity) */}
    <Link
      to={`/photos/${id}`}
      state={{ fromFeed: true }}
    >
      View Photo
    </Link>
  </div>
)

const Photo = ({ location, photoId }) => {
  if (location.state.fromFeed) {
    return <FromFeedPhoto id={photoId} />
  } else {
    return <Photo id={photoId} />
  }
}

Upvotes: 1

Related Questions