React Component Styling (Sass and CSS Module) and Integrating API

Getting Astronomy Picture of the Day from NASA

(Translator note: This is a translated version of an article “리액트 컴포넌트 스타일링, API 연동 실습 — NASA 에서 오늘의 우주 사진 가져오기” written by 김민준)

In this tutorial we will practice React Component Styling using Sass and build an app showing Astronomy Picture of the Day provided by NASA via an open API. You can find the final code here:

In a React project, there are a few ways of component styling, in this article, we will practice the combination of Sass and CSS Module. To use NASA open API, we will use Axios to handle simple web request.

#1. Set Up Working Environment

Create a boilerplate project using create-react-app

$ create-react-app nasa-apod

Eject the project configuration to customize project settings to use Sass

$ cd nasa-apod
$ yarn eject

Install required modules

$ yarn add axios classnames sass-loader node-sass include-media open-color better-react-spinkit react-icons moment
  • axios — Promise based HTTP request client
  • classnames — A helper library setting CSS Module and conditional className
  • sass-loader, node-sass: To use Sass in the project
  • include-media, open-color: Sass libraries (responsive design, color pallet)
  • better-react-spinkit: A component being displayed while loading
  • react-icons: SVG format React components library
  • moment: Date&Time library

Apply Sass + CSS Module

We will use both Sass and CSS Module in this project. Open webpack.config.dev.js under config directory and find the style-loader part.

Note that we added includePaths in sass-loader so that we can use @import 'utils' instead of import ../../styles/utils when we need utility functions commonly used in Sass. For this, paths.styles needs to be configured in config/paths.js.

Let’s try yarn start to start dev server. No problem?

Initialize Project

Let’s remove unnecessary files. Remove the following:

  • src/App.css
  • src/App.test.js
  • src/logo.svg
  • src/index.css

Let’s modify code accordingly. First, empty App component.

src/App.js

Then, remove code importing index.css in index.js

src/index.js

Style utils and base style settings

Let’s create base file for component based styling and utils file. Create styles directory in src directory and utils.scss file in the created folder.

src/styles/utils.scss

@import '~open-color/open-color';
@import '~include-media/dist/include-media';

~ in the path means to use a directory under node_modules.

We imported helper libraries for color pallet and responsive design. They are not mandatory in this project, but I chose to use them to show an example of using such libraries along with Sass.

Create base.scss like below.

src/base.scss

@oc-gray-8 is a color variable registered in open-color. Refer to a guide and write a variable name using a color and a number like this: @oc-[color]-[number]

Let’s import it in index.js.

src/index.js

Check if the page’s background is gray.

#2. Prepare Component Templates

We will create three components. And each component resides in a dedicated directory since we will apply CSS Module and Sass to the component. Each directory will have the following three files.

  1. ComponentName.js
  2. ComponentName.scss
  3. index.js

index.js imports and exports a component so that we can import the component using src/components/ComponentName instead of src/components/ComponentName/ComponentName. Like this:

export { default } from './ComponentName'

Each component file has the following template.

Now, let’s create each component’s template.

ViewerTemplate

This component is a template component that takes JSX formatted props; viewer and spaceNavigator and renders them on proper positions.

Create the following files

  • src/components/ViewerTemplate/ViewerTemplate.js
  • src/components/ViewerTemplate/ViewerTemplate.scss
  • src/components/ViewerTemplate/index.js

src/components/ViewerTemplate/ViewerTemplate.js

src/components/ViewerTemplate/ViewerTemplate.scss

.viewer-template {}

src/components/ViewerTemplate/index.js

export { default } from './ViewTemplate'

Viewer

This component shows an image or a video. It takes mediaType which could be ‘video’ or ‘image’ and url to the media as well as loading to displaying loader indication as props.

  • src/components/Viewer/Viewer.js
  • src/components/Viewer/Viewer.scss
  • src/components/Viewer/index.js

src/components/Viewer/Viewer.js

src/components/Viewer/Viewer.scss

.viewer {}

src/components/Viewer/index.js

export { default } from './Viewer'

SpaceNavigator

This component has buttons to navigate back and forth. It takes onPrev and onNext that will be connected to the buttons as props.

The reason why we chose SpaceNavigator instead of Navigator is Navigator already exists in the browser.

  • src/components/SpaceNavigator/SpaceNavigator.js
  • src/components/SpaceNavigator/SpaceNavigator.scss
  • src/components/SpaceNavigator/index.js

src/components/SpaceNavigator/SpaceNavigator.js

src/components/SpaceNavigator/SpaceNavigator.scss

.space-navigator {}

src/components/SpaceNavigator/index.js

export { default } from './SpaceNavigator'

Phew, now we are done with templates. If you want more convenient way for creating three files per component, try generate-react-component VS code extension. (We will talk about it later. It needs a little customization)

#3. Complete ViewerTemplate Component

Let’s complete ViewerTemplate

src/components/ViewerTemplate/ViewerTemplate.js

We put space-navigator-wrapper inside of viewer-wrapper since positioning SpaceNavigator component will be based on the size of view-wrapper.

src/components/ViewerTemplate/ViewerTemplate.scss

Let’s import it in App and use it!

src/App.js

You will see something like below.

#4. Complete SpaceNavigator Component

In this component, we will create two buttons on left and right using icons from react-icons. Here is the list of icons provided by react-icons.

src/components/SpaceNavigator/SpaceNavigator.js

src/components/SpaceNavigator/SapceNavigator.scss

After completing the component, import it from App and set it in ViewerTemplate as spaceNavigator prop. Don’t forget < and /> around the component name.

Now we can see arrows on the left and right of the page.

#5. Complete Viewer Component

NASA Open API returns two types of data.

Sometimes, it has ‘video’ type with Youtube URL.

{   
"media_type": "video",
"url": "https://www.youtube.com/embed/uj3Lq7Gu94Y?rel=0"
}

Or, it has ‘image’ type with an image URL.

{   
"media_type": "image",
"url": "https://apod.nasa.gov/apod/image/1712/GeminidsYinHao1024.jpg"
}

Before writing Viewer component, let’s inject ‘image’ type data to Viewer component from App.

src/App.js

Let’s render in Viewer component based on the provided props.

src/components/Viewer/Viewer.js

src/components/Viewer/Viewer.scss

You will see the result like below.

What a cool space picture! This time, let’s render a video. Change the props to Viewer component like below.

When a video is passed in, render it using iframe tag.

src/components/Viewer/Viewer.js

Let’s apply styles to iframe.

src/components/Viewer/Viewer.scss

Now, you can see a video in a proper size.

#6. Using API

Request API Key

For now, NASA Open API has 1,000 requests/day limitation. If all readers of this tutorial use a same API key, the number of requests probably exceed the limitation, so the API key stops working for the day. Therefore, request your own API key to proceed.

Go to NASA API page and find API key request form. A key will be generated after submitting the form. Also, it will be delivered to your email.

Form

Email

The red text is your API key.

What is Promise?

We will use a Promise based Web request library, Axios in this tutorial. Promise is an object handling asynchronous process in ES6. For example, I will show code to print out a number in console after one second. (Try the code below in Chrome development console. Entering new line is Shift + Enter).

Calling printLater function prints the number out after one second. This time, write code to print 1, 2, 3, 4 on each second.

If there are many asynchronous calls, the code structure will be deeply nested and the code readability will be very bad. We call this ‘Hell of Callbacks’.

Promise is a savior to save us from this problem in Javascript ES5. Let’s solve the problem using Promise. Additionally, we will use arrow function for better readability.

Output:

1
2
3
4
5
number is greater than 5

Build API Request function

Create api.js in src/lib directory. In this project, it’s okay to define these functions in App, however, since most projects have many API functions to handle, maintaining them in a separate file is recommended.

src/lib/api.js

Using API function

Let’s use API function we just created. Request a call to API from a component and use Promise’s then to do specific work when API responds.

src/App.js

If we don’t pass date, API will return the today’s picture.

Open the developer console and check if response comes in correctly.

To work on async process, Promise is more than enough, however, there is a more convenient way; using async/await.

To use async, we need to put async keyword at the function definition. And, use await keyword in front of Promise.

src/App.js

Check if response comes in as last time.

State management

Let’s manage component state based on response.

src/App.js

  • On start of request, set loading to true, and to false on end of it.
  • Since there is no data after today, set maxDate.
  • While destructuring the response, we assign new variable names. For example, the above code means we will call ‘media_type’ of response.data as ‘mediaType’.
  • Pass url, mediaType and loading to Viewer component.

Navigate Next / Previous image

Now we will work on navigating next and previous images. We can navigate images of Yesterday and the day before Yesterday from the initial date (today), and can navigate data of Tomorrow and the day after Tomorrow from a date in the past. The format of date is YYYY-MM-DD. We can use Date object by adding/subtracting a date and printing it in our desirable format, however, we will use moment in convenience.

Implement handleNext and handlePrev methods, and connect them to onNext and onPrev of SpaceNavigator component.

src/App.js

Try the left and right buttons on the page. Does the page change well? How cool it is!

#7. Displaying loader while loading

We will implement loader while loading data. There are a bunch of ways to do this by using SVG, CSS, etc. However, we will use better-react-spinkit library for minimum effort.

It’s easy to implement. If loading prop is true, render a loader in Viewer component. We can specify color and size via props. (There are many different types. Check the link and choose whatever you prefer.)

src/components/Viewer/Viewer.js

You will see a cool loader while loading!

Final Notes

In this tutorial, we learned how to compose components, apply styles to them and integrate Open API. In this project, we had all application states in App.js since it’s tiny, however, it’s not recommended due to size of code and code readability.

For example, if an app has multiple pages, there might be shared states across the pages.

Now, it’s time to learn Redux! (An article related to Redux is here (Original post in Korean).

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade