skip to Main Content

WordFlix – Single Page App tutorial: Step 7 – GraphQL Queries

Post Series: WordFlix – Super Powered Single Page App Tutorial

Step 7 – Starting Point

  • If you followed the previous steps of the tutorial, you should be able to continue here, but if you’re just jumping in or got lost in the last steps or just want to skip ahead, you can either check-out this branch of the Github repo or change directories to the “6” theme (wp-content/themes/6)

Write our first GraphQL Query

For the movies page we know we need a list of posts, and for each post we need the following:

  • ID (for linking to the movie detail page)
  • Title
  • posterUrl (featured image url)

So, we can use the GraphiQL desktop app to write and test our query.

Then, we can connect it to our MoviesPage component.

Import dependencies

In the MoviesPage.js file, add this to the imports:

import { gql, graphql } from 'react-apollo';

Then, below the MoviesPage component, add the following:

/**
 * Write the GraphQL query to get the list of movies.
 */
const moviesQuery = gql`
  query moviesList{
    posts {
      edges {
        node {
          id
          title
          featuredImage {
            sourceUrl
          }
        }
      }
    }
  }
`;

/**
 * Connect the GraphQL query with our MoviesPage component
 */
const MoviesPageWithData = graphql(moviesQuery)(MoviesPage);

/**
 * Export our component that is connected to Apollo
 */
export default MoviesPageWithData;

This uses the query we created in GraphiQL, then connects in to our MoviesPage component. Then finally, we change the export of the file to be the component with data.

Now, we have a MoviesPage that’s actually connected to GraphQL. You can open your React Dev Tools and see that the component has actual data fetched from WordPress!

Map over the list of movies

Now we need to render the data that Apollo has connected to our component.

Let’s update our MoviesPage component like so:

class MoviesPage extends Component {
  render() {

    /**
     * Get the data out of the props. "data" is a prop that Apollo provides
     * which includes the data returned from the GraphQL query
     */
    let { data } = this.props;

    /**
     * Apollo sets data.loading to true if the query is being fetched an we're waiting for data
     * to be returned. If this is the case, we can show the user some feedback that
     * the content is loading. And if it's not loading, we can render the actual data.
     */
    if ( data.loading ) {
      return <div>Loading...</div>
    } else {
      return (
        <div>
          <PageTitle>Movies</PageTitle>
          <MoviesWrapper>
            <Row gutter={40} type="flex" justify="space-between" align="center">
              {
                data.posts.edges.map((edge, i) => {
                  return (
                    <Col key={i} xs={24} sm={12} md={8} lg={6}>
                      <MovieCard movie={edge.node} key={i}/>
                    </Col>
                  )
                })
              }
            </Row>
          </MoviesWrapper>
        </div>
      );
    }
  }
}

This extracts the “data” out of the props that Apollo is passing to the component.

It then checks whether Apollo is set at a loading state and if so renders a div with the word Loading, otherwise it renders our component, but this time instead of a hard-coded list of components making up our grid, we now map through the posts and return a MovieCard for each item in the data set, and we pass the “node” to the MovieCard component as the “movie” prop.

Render the data

Now, let’s update our MovieCard component to render the data that we’re passing to it in the “movie” prop.

class MovieCard extends Component {

  /**
   * Render a Card component with the Movie poster and title
   */
  render() {

    /**
     * Get the movie from the props that were passed to the component
     */
    let { movie } = this.props;

    /**
     * Set the posterUrl using placehold.it as a fallback
     * @type {string}
     */
    let posterUrl = 'http://placehold.it/236x332';
    if ( movie.featuredImage && movie.featuredImage.sourceUrl ) {
      posterUrl = movie.featuredImage.sourceUrl;
    }

    return(
      <Card style={{marginBottom: '40px', flex:1}} bodyStyle={{ padding: 0 }}>
        <Link to="movies/movieId" >
          <div>
            <img src={posterUrl} width="100%" alt={"poster for " + movie.title}/>
            <MovieTitle>{movie.title}</MovieTitle>
          </div>
        </Link>
      </Card>
    );
  }
}

This extracts the “movie” from the props, then sets a posterUrl, either defaulting to a placehold.it url, or using the featuredImage.sourceUrl if it’s set.

Then, we replace the <img> src, the alt tag and the contents of <MovieTitle>

And now we have a MoviesPage rendering data from WordPress!

Connect the MoviePage

Next, let’s connect the MoviePage to GraphQL.

Link to the MoviePage

While we’re still in the MoviesPage file, update the Link in the MovieCard component like so:

<Link to={"movies/" + movie.id } >

This will now link from the MoviesPage to the movie detail with the ID of the movie in the URL. We’ll use that URL to populate the MoviePage with data.

Write the MoviePage Query

We can use GraphiQL to create the query for the Movie Page as well. We know we’ll want the following data:

  • ID
  • Title
  • Content
  • Poster URL (featuredImage)

Here, you’ll notice we’re using “query variables”

GraphQL uses static strings as the queries, which has a lot of benefits, so variables aren’t part of the query itself but are a separate part of the request. The Query string declares what variables will be used and what type of data the variable should be, then variables are sent next to the query string.

We can use this query we just created to connect our component.

Import dependencies

Import the dependencies we’ll need to connect the GraphQL query to our component:

import { gql, graphql } from 'react-apollo';

Connect the Query to the component

Now, let’s add our query and connect it to the component, and we’ll set the “id” variable as the movieId that’s in the URL.

const movieQuery = gql`
    query getMovie($id:ID!){
      post(id:$id){
        id
        title
        content
        featuredImage{
            sourceUrl
        }
      }
    }
`;

const MoviePageWithData = graphql(movieQuery, {
  options: ({routeParams}) => ({
    variables: {
      id: routeParams && routeParams.movieId ? routeParams.movieId : null,
    }
  })
})(MoviePage);

export default MoviePageWithData;

This adds the Query, then connects it to our MoviePage component, but gets the movieId out of the React Router routeParams, which are stored in Redux. That ID is used in our GraphQL query to fetch the proper data from WordPress to populate the page.

Apollo will grab data from Redux if it’s available before making a network request, so if we’ve already fetched the data we need and stored it in Redux, we’ll be able to view the page without making a single network request. Neat!

Now, we have a working MoviePage.

 

Leave a Reply

Share This
Back To Top