JS-201-ReactFundamentals
  • Part 0: App Overview
  • Part 1: Intro to React
    • 1.0: Packages Setup
    • 1.1: Project Setup
    • 1.2: React Router Dom
  • Part 2: JavaScript Concepts
    • 2.0: ES6 Overview
    • 2.1: classes
    • 2.2: constructors
    • 2.3: extends
    • 2.4: super
    • 2.5: interpolation
    • 2.6: map
    • 2.7: filter
  • Part 3: Functional Components
    • 3.0: Functional Components Overview
    • 3.1: Calling Functional Components
    • 3.2: Single Element Rule
    • 3.3: Arrow Functions
    • 3.4: Challenge
    • 3.4: Solution
    • 3.5: Challenge 2
    • 3.5: Solution 2
  • Part 4: JSX Challenge
    • 4.1: JSX Overview
    • 4.1: Challenge Answer
    • 4.2: className
  • Part 5: Class Concepts
    • 5.1: State
    • 5.2: setState
    • 5.3: Class Components Challenge
    • 5.4: Class Components Challenge Answer
  • Part 6: Props Starter
    • 6.0: Props Overview
    • 6.1: Props Demo and Challenge 1
    • 6.2: Props Answer 2
    • 6.3: Props Passing Functions and Challenge 2
    • 6.4: Props Answer 2
    • 6.5: External Props and Mapping Components
    • 6.6: PropTypes
  • Part 7: Lifecycle Methods
    • 7.0: Lifecycle Overview
    • 7.1: Lifecycle Methods
    • 7.2: Mounting Methods
    • 7.3: Update Methods
    • 7.4: Unmount Methods
  • Part 8: Apps
    • 1.0 - Small Timer Apps
      • 1.1 - Simple Timer
      • 1.2 - Clock App
      • 1.3 - Stop Watch App
      • 1.4 - Challenge
    • 2.0 - Concept List
      • 2.1 - Concept Data
      • 2.2 - Concept App Component
      • 2.3 - Concept Component
      • 2.4 - Concept List Component
    • 3.0 - NYT
      • 3.1 - NytApp Component
      • 3.2 - Nyt Results
    • 4.0 - The Friend List App
      • 4.1 - Friend List App Component
      • 4.2 - Friend Component
    • 5.0 - Movie Search Application
      • 5.1 - Form Component
      • 5.2 - Form Results Component
      • 5.3 - Styled Components
    • 6.0 - YouTube Api
      • 6.1 - Video Component
      • 6.2 - Video Component
      • 6.3 - Video Component
    • 7.0 - Github Api Application
      • 7.1 - The Users
      • 7.2 - Github API Component
      • 7.3 - Github API Card
      • 7.4 - Github API Card Form
      • 7.5 - Github API Card List
      • 7.6 - Github API Search
      • 7.7 - Github API Challenge
    • 8.0 - Bitcoin Api Application
      • 8.1 - Bitcoin API Setup
      • 8.2 - Bitcoin API Fetch
      • 8.3 - Bitcoin API Line Chart
      • 8.4 - Bitcoin API Fetching Data
      • 8.5 - Bitcoin API Info Box
      • 8.6 - Bitcoin API Completed Code
    • 9.0 - Google Maps Api Challenge
    • 10.0 - Sound Cloud App Challenge
    • 11.0 - VR App Challenge
    • 12.0 - React Native Intro
  • Part 9: Project
  • Part 10: Notes
    • 10.1 - Resources
    • 10.2 - Troubleshooting
Powered by GitBook
On this page
  • Creating our Component
  • Back to NytApp
  • Formatting & Displaying our Results
  • Pagination
  • Buttons to Change Page
  • One More Thing
  1. Part 8: Apps
  2. 3.0 - NYT

3.2 - Nyt Results

Creating our Component

We need to set up a component, create a file called, NytResults.js to display our results!!! Since we've handled all of the "smarts" of the applications in our parent component, we can make this a functional component. The purpose of this component is to take the results and nicely render them. Let's start our component.

import React from 'react';

const NytResults = (props) => {
  return (
    <div>
    </div>
  )
}

By passing props in the parentheses of our component, we can now access the props in our component, which we'll need because we're going to need to get our results information from our parent component. Right now, we haven't set that up, so we'll need to do that.

Back to NytApp

In our NytApp component, we have our results information saved in the state. If we want to render that information in our newly created NytResults component we need to pass it as a prop to NytResults so that it can access it. But first in order to even use NytResults in NytApp we need to import it. Add the following import to your NytApp.js file.

import NytResults from './NytResults'

Cool, now we can use it in our render!! Directly underneath our closing </form> tag, we can now use our NytResults component.

{
    this.state.results.length > 0 ? <NytResults results={this.state.results} /> : <div></div>
}

Let's think through this code. First off notice that we're using another ternary, this time the condition we're checking for is if our results length is greater than zero, AKA do we have any results. If we do have results, then we want to display our NytResults component, if we don't then we can just display an empty div.

Notice the props that we are passing to the NytResults component. We're setting the prop results equal to this.state.results. So in order to access this prop in the NytResults component, we need to use props.results since that's what we named our prop. If we named our prop nytResults instead of results then we would have to use props.nytResults. But we didn't so we'll use just results.

Formatting & Displaying our Results

Let's set up our return to display something other than an empty <div>. We're going to use our favorite, the super useful .map() to nicely format our response. Instead of having to do for loop inside of for loop like in vanilla JS, we can just use map to accomplish the same thing. Make your NytResults component look like this:

import React from 'react';

const NytResults = (props) => {
  return (
    <div>
      {props.results.map(result => {
        return (
          <div key={result._id}>
            <h2>{result.headline.main}</h2>
            {result.multimedia.length > 1 ? <img alt="article" src={`http://www.nytimes.com/${result.multimedia[1].url}`} /> : ''}
            <p>
              {result.snippet}
              <br />
              {result.keywords.length > 0 ? ' Keywords: ' : ''}
            </p>
            <ul>
              {result.keywords.map(keyword => <li key={keyword.value}>{keyword.value}</li>)}
            </ul>
            <a href={result.web_url}><button>Read It</button></a>
          </div>
        )
      })}
    </div>
  )
}
export default NytResults

Let's walk through this code. We're taking props.results and calling map on it. Reminder, map essentially takes every item in an array and performs the same action on it. So in this case, map is taking each result, and formatting it in JSX. Things specifically to notice:

  • unique keys are required when you create multiples of the same item (so when you use map). Luckily we have an id to use here.

  • We're taking the main headline and putting it in an h2 tag

  • We use another ternary here to check if there is a more than 1 (AKA at least 2) things in the multimedia property. If there is at least 2, we want to display the image in the 2nd place [1] because that is a "large" size and not the "xl" size of [0].

  • We put the snipper in a <p> tag

  • We have another ternary to check if there are keywords. If there are we display the string ' Keywords: '.

  • Then we use a list, and another map to put every keyword into a list item

  • Lastly, we make a link to the url of the article.

Hopefully you can see how much more streamlined this is than the vanilla JS version.

Pagination

Now, we've got our results displaying, but we currently have no way to look at anything but the 10 articles. If you remember back to the vanilla JS version we had pagination. So let's put that in place in our React app! First thing is, let's add a way to paginate to our parent component. If you look at your vanilla JS code, we had two separate functions for different directions of pagination, here we can just use one.

Add this function right above the render().

  changePageNumber = (e, direction) => {
    e.preventDefault()
    if (direction === 'down') {
      if (this.state.pageNumber > 0) {
        let newPageNumber = this.state.pageNumber - 1
        this.setState({ pageNumber: newPageNumber })
        this.fetchResults();
      }
    }
    if (direction === 'up') {
      let newPageNumber = this.state.pageNumber + 1
      this.setState({ pageNumber: newPageNumber })
      this.fetchResults();
    }
  }

In changePageNumber we are taking in the event, so we can preventDefault, and direction. The direction is going to tell us up or down, then we can incorporate the checks we need to. If the direction is down, we need to make sure that the pageNumber is not 0 so we don't go into the negatives. If it's up we can just add 1! Then we're just fetching results again, after we changed our state to reflect the current correct page number.

Now that we have our method defined, we need a way to actually use it with our results. Since we only want the buttons for pagination to appear if we have results, let's put it in our NytResults component. In order for us to be able to access it inside of that component we need to pass it as a prop! Let's update where we call NytResults in our render to look like this:

{
this.state.results.length > 0 ? <NytResults results={this.state.results} changePage={this.changePageNumber} /> : <div></div>
}

Now, we are passing our function changePageNumber to NytResults as a prop called changePage. So we'll need to use it as props.changePage because that's what we named it.

Buttons to Change Page

Let's go to our NytResults component and add the ability to call the method we just created in the parent component. Beneath our map, let's add the following code:

<div>
    <button onClick={e => props.changePage(e, 'down')}>Previous 10</button>
    <button onClick={e => props.changePage(e, 'up')}>Next 10</button>
</div>

Here we're just creating two buttons, one that says Previous 10 and one that says Next 10. They're both calling the changePage function we defined in the parent, and passing the corresponding direction. Hopefully you can see how we passed the method down to this child component through props.

Now, our app should work and be able to paginate!!!

One More Thing

So now when we change pages, it works, but what happens if we go to page 5 of one search, and want to search something else? We need make it so that when someone does a new search, the pageNumber goes back to 0 every time, so they'll start on the first page of results.

Make your handleSubmit method look like this:

  handleSubmit = (event) => {
    this.setState({pageNumber: 0})
    this.fetchResults()
    event.preventDefault()
  }

That's it! You should now have a nicely working React application. Hopefully you can see some of the advantages of React over vanilla JS! Double check the completed code below. Also, feel free to style things however you want in this app.

import React from 'react';

const NytResults = (props) => {
  return (
    <div>
      {props.results.map(result => {
        return (
          <div key={result._id}>
            <h2>{result.headline.main}</h2>
            {result.multimedia.length > 1 ? <img alt="article" src={`http://www.nytimes.com/${result.multimedia[1].url}`} /> : ''}
            <p>
              {result.snippet}
              <br />
              {result.keywords.length > 0 ? ' Keywords: ' : ''}
            </p>
            <ul>
              {result.keywords.map(keyword => <li key={keyword.value}>{keyword.value}</li>)}
            </ul>
            <a href={result.web_url}><button>Read It</button></a>
          </div>
        )
      })}
      <div>
        <button onClick={e => props.changePage(e, 'down')}>Previous 10</button>
        <button onClick={e => props.changePage(e, 'up')}>Next 10</button>
      </div>
    </div>
  )
}
export default NytResults
import React, { Component } from 'react';
import NytResults from './NytResults'

const baseURL = 'https://api.nytimes.com/svc/search/v2/articlesearch.json';
const key = 'yourApiKeyHere1234567890abcdefghijkl';

export default class NytApp extends Component {
  constructor(props) {
    super(props)
    this.state = {
      search: '',
      startDate: '',
      endDate: '',
      pageNumber: 0,
      results: []
    };
  }

  handleChange = (event) => {
    this.setState({
      [event.target.name]: event.target.value,
    });
  }

  handleSubmit = (event) => {
    this.setState({pageNumber: 0})
    this.fetchResults()
    event.preventDefault()
  }

  fetchResults = () => {
    let url = `${baseURL}?api-key=${key}&page=${this.state.pageNumber}&q=${this.state.search}`
    url = this.state.startDate ? url + `&begin_date=${this.state.startDate}` : url
    url = this.state.endDate ? url + `&end_date=${this.state.endDate}` : url
    fetch(url)
      .then(
        (response) => response.json()
      ).then((data) => {
        this.setState({ results: data.response.docs })
      })
  }

  changePageNumber = (e, direction) => {
    e.preventDefault()
    if (direction === 'down') {
      if (this.state.pageNumber > 0) {
        let newPageNumber = this.state.pageNumber - 1
        this.setState({ pageNumber: newPageNumber })
        this.fetchResults();
      }
    }
    if (direction === 'up') {
      let newPageNumber = this.state.pageNumber + 1
      this.setState({ pageNumber: newPageNumber })
      this.fetchResults();
    }
  }

  render() {
    return (
      <div className="main">
        <div className="mainDiv">
          <form onSubmit={e => this.handleSubmit(e)}>
            <span>Enter a SINGLE search term (required): </span>
            <input type="text" name="search" onChange={this.handleChange} required /><br />
            <span>Enter a start date: </span>
            <input type="date" name="startDate" pattern="[0-9]{8}" onChange={this.handleChange} /><br />
            <span>Enter an end date: </span>
            <input type="date" name="endDate" pattern="[0-9]{8}" onChange={this.handleChange} /><br />
            <button className="submit">Submit search</button>
          </form>
          {
            this.state.results.length > 0 ? <NytResults results={this.state.results} changePage={this.changePageNumber} /> : <div></div>
          }
        </div>
      </div>
    );
  }
}
Previous3.1 - NytApp ComponentNext4.0 - The Friend List App

Last updated 7 years ago