Build and Deploy your first REACT app using the NASA api
Build a gallery app using React v18 and the NASA APOD API
Today we'll be building this Nasa photo archive app with the latest version of React to this date:
We'll be practicing props, using hooks, fetching data, displaying it, mapping, and many other fun stuff so fasten your seatbelt!
Here's the completed code: github.com/YuriCodes/NASA-REACT-app
Live version: nasaphothoarchiveapp.netlify.app
Setting up the environment
We'll start by creating a new React app, to do this go to your Command Line and cd over to the folder you're going to create your application and type npx-create-react-app [your app name] and hit enter, this will work if you have Node >= 14.0.0 y npm >= 5.6.
cd Desktop => cd [app folder] => npx create-react-app [your app name]
If that doesn't work type "npm init" this will create a file package. json and then type npx-create-react-app [your app name]
cd Desktop => cd [app folder] => npm init => npx create-react-app [your app name]
If you were succesful all we need to do is cd over to our app and type npm start.
Get the styling from the App.css file in the GitHub repo
API KEY and NASA API
The API we'll be using is NASA's APOD API and we'll need a key you can get it here: api.nasa.gov
This is the documentation we'll be referring to in this tutorial: github.com/nasa/apod-api#getting_started
For this tutorial i'll be posting my API key but you should NEVER publish them
Now that we have our API KEY we can continue with the tutorial
Fetching the data
Let's start by opening our app folder in our code editor, in my case VSCode and open our App.js file.
- We're going to be using the useState and useEffect hooks
- Fetching the data from our API and setting it as the new value to our data variable
import React, {useEffect, useState} from "react";
function App() {
const [data, setData] = useState([]);
useEffect(() => {
fetch("https://api.nasa.gov/planetary/apod?api_key=N19AA6NPWqnzmEEazzX9ZamcIOcTgTfvqL1LKaf3")
.then(res => res.json())
.then(data => {
setData(data)
})
.catch(err => {
console.log(err)
})
}, [])
return (
<div className="App">
</div>
);
}
export default App;
We now need a way to see the data we're fetching. I'm quite a visual learner so I would rather see the fetched data so let's do just that!!
We're going to return a div accessing the values we got from our data json. How do i know the values to access?? From the API documentation.
return (
<div className="App">
<h1>{data.title}</h1>
<img src={data.hdurl}></img>
<h2>{data.explanation}</h2>
</div>
);
It should be looking something like this, don't worry, the image, title and description might vary.
Reusable functional components
But we're accessing that data manually; let's map through it and create a reusable Card component we can pass props so it dynamically generates new cards for each image.
Since we're only rendering one image, we're going to have to add the parameter "count" to our useEffect hook fetch url. To the count parameter you can set any value no greater than 100. Let's use 6.
fetch("https://api.nasa.gov/planetary/apod?api_key=N19AA6NPWqnzmEEazzX9ZamcIOcTgTfvqL1LKaf3&count=6")
Now let's create a Card component!! We'll make a file called Card.jsx, we'll add a function called Card to which we'll pass the props from our App.js.
It's important to remember to use the exact same name we're passing as props from our App.js so let's see which props we'll pass to our Card component. We need the title, the image, description, date, and copyright.
Let's fix our return statement on our App.js these are the values we'll be sending to our Card component from our App.js.
{data.map((nasaData, index) =>{
return(
<Card
id={index}
title={nasaData.title}
img={nasaData.hdurl}
explanation={nasaData.explanation}
onClick={openCaption}
copyright={nasaData.copyright}
/>
)
})}
We can now create our Card component in Card.jsx which will be receiving the props sent over by our App.js
import React from "react";
function Card(props){
return(
<div className="Card-Component">
<h1 className="Title-of-card"> {props.title}</h1>
<img className="Card-img" src={props.img} alt="nasa gallery"></img>
<h2 className="Img-description" onClick={props.onClick}>
{props.explanation} </h2>
<h3 className="Img-Date">{props.date}</h3>
<h4 className="Img-copyright"> {props.copyright} </h4>
</div>
)
}
export default Card;
Don't forget to import it on the top of our App.js file
import Card from './Card'
Now we're dynamically creating new cards !! But the description is too long and it's looking quite crowded; let's use a ternary operator to tell React when to open the description and when to close it.
We'll need to set a new state on our app.js.
const [showMore, setShow] = useState(false);
This means that the initial state of our description is closed (false), to change that we're going to need a function that changes the state to true (open) when clicked. We're also going to set a time out so that our description will close after a while, for testing purposes i used 5000 but you can use whichever amount of time you want.
function openCaption(){
setShow(true)
setTimeout(() =>{
setShow(false)
}, 5000)
}
Now for it to activate on click we need to go back to our return statement in our app.js and pass it as a prop to our Card as an onClick event because we want the full description to show only when clicked.
We can achieve this with a ternary operator in our explanation element; if set to true, it'll show the whole caption, but if not it'll show the truncated string making our app look better.
{data.map((nasaData, index) =>{
const capt = nasaData.explanation; // Here
const truncated = capt.substring(0,100) // Here
return(
<Card
id={index}
title={nasaData.title}
img={nasaData.hdurl}
explanation={showMore ? capt : truncated} // Here
onClick={openCaption}
copyright={nasaData.copyright} />
)
})}
Now the last step is passing this onClick function to our Card component as an onClick event.
function Card(props){
return(
<div className="Card-Component">
<h1 className="Title-of-card"> {props.title}</h1>
<img className="Card-img" src={props.img} alt="nasa gallery"></img>
<h2 className="Img-description" onClick={props.onClick}> //Here
{props.explanation} </h2>
<h3 className="Img-Date">{props.date}</h3>
<h4 className="Img-copyright"> <FaUser /> {props.copyright} </h4>
</div>
)
}
export default Card;
It's looking quite good! Let's add a Header and a Footer the same way we added our Card component.
First create a Header.jsx file
The images are available on my GitHub at the start of this article
import React from "react";
import Nasa from '../src/nasa.png'
function Header(){
return(
<div>
<img src={Nasa} alt="nasa logo"className="nasa-logo"></img>
<h1 className="Header-txt">NASA's Photo Archive</h1>
</div>
)
}
export default Header;
Now for the Footer, we need a button to load more images, how could we do this?? You got it! Adding a click event and passing it as props.
In our Footer.jsx:
import React from "react";
function Footer(props){
return(
<footer className="footer-section">
<button className="footer-btn" onClick={props.onClick}>Load More?</button>
<hr />
<h5 className="footer-txt1"> Coded by <a href="https://twitter.com/yuricodesbot" rel="noreferrer" target="_blank">Yuricodesbot</a></h5>
<h6 className="footer-txt2">Photos by <a href="https://github.com/nasa/apod-api#getting_started">Nasa </a></h6>
</footer>
)
}
export default Footer;
Now back in our App.js file, we need a function to trigger when the onClick from the footer is activated, let's keep it simple and refresh the page.
function loadMore(){
window.location.reload(false)
}
Don't forget to also import both the Header and Footer components
import Header from './Header'
import Footer from './Footer'
And we update our return statement to include this components like so:
return (
<div className="App">
<Header />
{data.map((nasaData, index) =>{
const capt = nasaData.explanation;
const truncated = capt.substring(0,100)
return(
<Card
id={index}
title={nasaData.title}
img={nasaData.hdurl}
explanation={showMore ? capt : truncated}
onClick={openCaption}
copyright={nasaData.copyright} />
)
})}
<Footer
onClick={loadMore}
/>
</div>
);
Our app should be looking like this now :
Let's give it the final touches so we can deploy it.
Adding icons
Let's go back to our terminal, hit Ctrl + C and copy this to install the icon library we'll be using in our Card component
npm install react-icons --save
Don't forget to start the server again
npm start
Now on our Card component we import the icon we'll be using
import {FaUser} from "react-icons/fa";
And add it to our Copyright section as a component
<h4 className="Img-copyright"> <FaUser /> {props.copyright} </h4>
Now we have this icon in our Copyright section!
I'd say we're ready to deploy it!!
Deploying our App
Well, you should be thinking about our API key. I said in the beginning you should NEVER post it, so how do we go around it? Well... On the client side, API keys are never completely save -it's best to keep them in the back end- but React can let us store it in a way that is less accessible by people using REACT_APP_VARIABLES.
Let's start creating a .env file in our root directory, as in the folder of your app. This will contain your API key. You MUST start the name as REACT_APP so that React knows it's an environment variable.
REACT_APP_API_VARIABLE=N19AA6NPWqnzmEEazzX9ZamcIOcTgTfvqL1LKaf3
Now back to our App.js we can access it by modifying the url in our fetch useEffect hook, using string concatenation. It MUST start with process.env. and be THE SAME NAME you used on the .env file.
fetch("https://api.nasa.gov/planetary/apod?api_key="+process.env.REACT_APP_API_VARIABLE+"&count=6")
When you make changes to environment variables always remember to restart the server!
Now to deployment!!
You can deploy it as you normally would, connecting the GitHub repo to Netlify, with the case of environment variables you need to select deploy settings:
And under Build & Deploy go to Environment
This will allow you to enter your environment variable and for it to be accessible when the site deploys. Just remember to set THE EXACT SAME NAME you have in your app.
Hit save and your site has deployed correctly!
React is such a fun library to work with, i really enjoyed making this tutorial and the video should be up soon on Youtube, thank you so much for reading! :]
Don't hesitate to contact me and let me know if you'd like to add something else in the comments.
☕If you enjoy my content Buy me a coffee It'll help me continue making quality blogs💕
💙Follow me on Twitter to know more about my self-taught journey!
💜 Web Design for Beginners Series
💜 JavaScript For Newbies Series
❤️ Also subscribe to my Youtube Channel !
🖤And for more content and tips on TikTok !