0

Gatsby/React noob here.

I have created a content type called Topic in Contentful. Each Topic contains a title, desc, and slug to the topic page. Currently, I am able to render each topic as a card on the page. What I am trying to figure out now is how to render only one card at a time and have the card content (title/desc) randomly change when clicking the spin button.

I created this quick demo to show what I am going for visually.

Here is where I am rendering each topic card:

import React from 'react'
import { graphql, navigate, StaticQuery } from 'gatsby'
import './topicFeed.scss'

export default () => (
   <StaticQuery
     query={graphql` 
     query TopicFeed {
  allContentfulTopic(filter: {featured: {eq: true}}) {
    edges {
      node {
        cardDescription {
          cardDescription
        }
        cardTitle
        id
        slug
      }
    }
  }
}
`}   
     render={data => ( 
      
          <div>
            <div className='feed'>
                {data.allContentfulTopic.edges.map(edge => (
                <div key={edge.node.id} className='topic__item'>
                    <h2 className='card__title'>{edge.node.cardTitle}</h2>
                    <div className="card__desc--container">
                        <p className='card__desc'>{edge.node.cardDescription.cardDescription}</p>
                    </div>
                    <div className='card__link' onClick={() => navigate(`/topic/${edge.node.slug}`)}>
                        <p>Learn More</p>
                    </div>
                </div>
                ))}
            </div>

            <button onClick={() =>console.log('werked')}onClick={() =>console.log('werked')}>SPIN</button>
        </div>
     )}
   />
 )

It's my understanding that gatsby renders the divs for each card on build so I am confused about how that data can be manipulated onClick by the user.

whoMe
  • 227
  • 2
  • 13
  • Gatsby doesn't need to "build" all of the different cards up front, it will just create the static markup for page routes, there is nothing to stop you showing one card at a time and have it randomised on click. Your page is already getting the full list of data, but you are using `.map()` to render all cards in the array. You probably want to just grab one card by array index, which you can pick using `Math.random()` based on the array length. – codeth Jun 29 '20 at 22:01

1 Answers1

-1

Try something like this. Instead of mapping the entire array of cards, display just one index. When the button is clicked, set a random card index.

import React, { useState } from 'react'
import { graphql, navigate, StaticQuery } from 'gatsby'
import './topicFeed.scss'

export default () => {
  const [cardIndex, setCardIndex] = useState(0)

  const getRandomCardIndexBetween = (min, max) => Math.floor(Math.random() * (Math.floor(max) - Math.ceil(min) + 1)) + min

  return (
    <StaticQuery
      query={graphql` 
          query TopicFeed {
              allContentfulTopic(filter: {featured: {eq: true}}) {
                  edges {
                      node {
                          cardDescription {
                              cardDescription
                          }
                          cardTitle
                          id
                          slug
                      }
                  }
              }
          }
      `}
      render={data => {
        const {
          cardDescription: {
            cardDescription
          },
          cardTitle,
          slug,
        } = data.allContentfulTopic.edges[cardIndex].node;

        return (
          <div>
            <div className='feed'>
              <div className='topic__item'>
                <h2 className='card__title'>{cardTitle}</h2>
                <div className="card__desc--container">
                  <p className='card__desc'>{cardDescription}</p>
                </div>
                <div className='card__link' onClick={() => navigate(`/topic/${slug}`)}>
                  <p>Learn More</p>
                </div>
              </div>
            </div>

            <button onClick={setCardIndex(getRandomCardIndexBetween(0, data.allContentfulTopic.edges.length - 1))}>SPIN</button>
          </div>
        )}}
    />
  )
}

Here's a jsfiddle showing how you could achieve this with plain React (no Gatsby/GraphQL)

codeth
  • 557
  • 3
  • 7
  • Your solution makes sense (ty for that) but when I implement it I'm getting this error: Error: Objects are not valid as a React child (found: object with keys {node}). If you meant to render a collection of children, use an array instead. – whoMe Jun 29 '20 at 23:25
  • Sorry, code wasn't tested. Did you find a solution in the end? You should be able to use the array index directly like `data.allContentfulTopic.edges[cardIndex]`, can use object destructuring to tidy it up a bit too. Will update my answer. – codeth Jul 02 '20 at 20:54