A ReactJS Clone of Dev.to: Working with React Hooks
Tags: react, javascript, api
Published: 2021-07-27 00:06:15

> Link to GitHub repo is now available. Check it out at the bottom of this article.
> Check the Live Site hosted on [GitHub Pages](https://maswerdna.github.io/dev-to-blog).

Recently, I needed to prepare a lecture material on Consuming REST APIs in your React Project so I decided to write about the dev.to API - available at https://docs.forem.com/api/. Below is what I put together while working on the project.

## Introduction
An API, or application programming interface, is a set of rules that define how applications or devices can connect to and communicate with each other. A REST API is an API that conforms to the design principles of the REST, or representational state transfer architectural style. [What is a REST API? | IBM](https://www.ibm.com/cloud/learn/rest-apis).

React is a Facebook owned, Opensource JavaScript library used to develop responsive and lightweight UIs.

Dev.to is a developer's blogging website described as a constructive and inclusive social network for software developers.

## What you should know
To be able to understand this tutorial, you should have basic knowledge of JavaScript and React.

## What you will learn in this part
  * [1. Creating a new react app using npm or yarn](#section-1)
  * [2. Working with React Hooks](#section-2)

<!-- * [5. Consuming API data](#section-5) * [6. Working with React Markdown library](#section-6) * [7. Working with SyntaxHighlighter](#section-7) * [8. Hosting React App on GitHub Pages](#section-8) * [9. A short note about Responsive Design](#section-9) * [10. Connecting your React App to a Node Backend](#section-10) -->

### 1. Creating a new react app using npm or yarn <a name= "section-1"></a>
> *This section is added as a refresher*

Before you can start building react apps you need to install the latest version of node on your development machine. `npm` and `npx` is bundled with the node installer. Download Node.js from the official website - [Download | Node.js](https://nodejs.org/en/download/)

After installation and setting up of your environment, you should be able to run the following command from your command line (CLI) - `npx create-react-app my-awesome-app`. Refer to the [React official documentation](https://reactjs.org/docs/create-a-new-react-app.html) or [Create React App website](https://create-react-app.dev/) for detailed instructions.

To learn about `yarn`, refer to [yarn official documentation](https://yarnpkg.com/lang/en/docs/).

Now that you have created your app, it's time to `cd my-awesome-app`. Good! You are now in your app directory.

### 2. Working with React Hooks <a name= "section-2"></a>
Navigate to your project folder and open the `src` directory, i.e. C:/path/to/my-awesome-app/src, and open the `index.js` file with your favourite editor. I use either SublimeText or VSCode.

Your html index file can be found at C:/path/to/my-awesome-app/public/index.html. We will need this file later when we are ready to push to GitHub pages.

If you open the html file in a browser, you would be greeted with a blank page. So, to start your app, run the following command: `npm start` or `yarn start` and wait for the development server to start your app in your default browser.

Your CLI commands so far would look like the following
```cli
    > npx create-react-app my-awesome-app
    > cd my-awesome-app
    > npm start
```

Once the server comes up, you would see the default React app loaded. It's now time to start building your own project by editing the `index.js` file that we opened earlier. Leave other files in the src directory for now. We will delete the ones we don't need later.

Delete the whole content of the index.js file and type the following:

***file: index.js***
```javascript
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

ReactDOM.render(
  <React.StrictMode>{/* This component will notify us of potential problems */}
      <App />
  </React.StrictMode>,
  document.getElementById('root')
);
```

Before we work on our App.js file, I would like us to create some components, so let's start with the navigation bar. You should access the dev.to homepage on a laptop to be able to see the navigation bar.

***The Navbar view at >= 640px***
![dev-navbar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1lhl8rb5ekvrf2grt3ra.jpg)
***The Navbar view at < 640px***
![dev-nav-mobile](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7qqkg6i0ytjhugdgshca.jpg)

***file: Navbar.js***
```javascript
import React from 'react';
import {Link} from 'react-router-dom';//npm install react-router-dom

function Navbar({query, onChange}) {
    return (
        <header className="flex header justify-between items-center p-2 px-3 m:p-0 m:px-0 m:pb-2">
            <h1 className="crayons-subtitle-2">Posts</h1>

            <nav className="crayons-tabs hidden s:flex" aria-label="View posts by">
                <ul className="crayons-tabs__list">
                    <li>
                        <Link data-text="Feed" to="/" className={"crayons-tabs__item" + (query === "/" ? ' crayons-tabs__item--current' : '')}>Feed</Link>
                    </li>
                    <li>
                        <Link data-text="Week" to="/top/week" className={"crayons-tabs__item" + (query === "/top/week" ? ' crayons-tabs__item--current' : '')}>Week</Link>
                    </li>
                    <li>
                        <Link data-text="Month" to="/top/month" className={"crayons-tabs__item" + (query === "/top/month" ? ' crayons-tabs__item--current' : '')}>Month</Link>
                    </li>
                    <li>
                        <Link data-text="Year" to="/top/year" className={"crayons-tabs__item" + (query === "/top/year" ? ' crayons-tabs__item--current' : '')}>Year</Link>
                    </li>
                    <li>
                    <Link data-text="Infinity" to="/top/infinity" className={"crayons-tabs__item" + (query === "/top/infinity" ? ' crayons-tabs__item--current' : '')}>Infinity</Link>
                    </li>
                    <li>
                        <Link data-text="Latest" to="/latest" className={"crayons-tabs__item" + (query === "/latest" ? ' crayons-tabs__item--current' : '')}>Latest</Link>
                    </li>
                </ul>
            </nav>

            <select className="right s:hidden f-16 pd5 b-r5 sh-focus" value={query} onChange={onChange}>
                <option value="/">Feed</option>
                <option value="/top/week">Week</option>
                <option value="/top/month">Month</option>
                <option value="/top/year">Year</option>
                <option value="/top/infinity">Infinity</option>
                <option value="/latest">Latest</option>
            </select>
        </header>
    )
}

export default Navbar;
```

The Navbar component accepts 2 props - query and onChange. The `query` **prop** holds the current value of the category of articles that was fetched. There are 6 categories - **feed, week, month, year, infinity and latest.**

The `onChange` **prop** points to the callback that runs each time we change the article category with the select element.

Note that the Navbar component contains 2 functional elements, the `nav` and the `select`. Both elements are used together wherever they appear on the website and they both act on the same information which is the *current article category*, hence, there's no need to extract them into separate components.

***Styling the Navbar component***
For brevity sake, I would skip all CSS codes in this article, except when it provides any functionality. The full code can be found in the project repo on GitHub.

***Responsiveness***
Dev.to has 4 break-points, viz:
0. 0 - 639 [Small to Medium Mobile devices]
1. 640 - 767 [Large Mobile devices]
2. 768 - 1023 [Tablet devices]
3. 1024 - 1280 and up [Laptops]

The design of Dev.to follows the Mobile-first approach and the break-point rules can be declared as shown in the codes below:

```css
*, *:before, *:after {
    /* Your general styles here */
    /* Styles for extra small devices */
}

@media screen and (min-width: 640px) {
    /* Takes care of small to medium devices */
}

@media screen and (min-width: 768px) {
    /* Takes care of tablet devices */
}

@media screen and (min-width: 1024px) {
    /* Takes care of laptop devices */
}
```

***Navbar Functionality***
We have used the Link component from `react-router-dom` to handle our links. **Please do not forget to `npm install react-router-dom`.** We will see why this is necessary in a moment. We have also added an `onChange` event listener to the `<select>` element to handle the action.

Now, let's write the Navbar controller. We would add this controller in our `App.js` file.

***file: App.js***
```javascript
import React, {useState, useEffect} from 'react';
import {Route, Switch, useHistory, useLocation} from 'react-router-dom';
// import Home from './Home';
// import Article from './Article';
// import Search from './Search';

function App() {
  const location = useLocation();// provided by the router bundle
  const history = useHistory();// provided by the router bundle

  const [posts, setPosts] = useState([]);
  const [failure, setFailure] = useState(false);
  const [query, setQuery] = useState(location.pathname);
  const [isShowing, setIsShowing] = useState(false);// for the Hamburger
  //
  function handleChange(event) {
    history.push(event.target.value); // adds a new entry to the history object
    // event.target.value could be one of "/, /top/week, /top/month, /top/year, /top/infinity, /latest"
  }
  function SideNavToggler() {// Hamburger Menu is the Side Navigator
    setIsShowing(isShowing => !isShowing);
  } // we use this state to decide whether the side menu should be revealed or hidden during the next click of the trigger element.
            //
  useEffect(() => {
    // 1. to avoid creating new object each time the component re-renders, we have to define this within the useEffect.
    // 2. if it was passed in a dependency, React will create new object each time, causing the effect hook to run repeatedly for every effect.
    // 3a. We are transforming the location pathname to something that the dev.to API understands, but we need to keep the path name SEO friendly.
    // 3b. dev.to/api/articles?top=7 gets us the articles created over the week, but we want to make it appear as https://dev-to-blog/top/week => https://dev.to/top/week - hence, the need for this mapping.
    const mymap = {
      '/': 0,
      '/top/week': 7,
      '/top/month': 30,
      '/top/year': 365,
      '/top/infinity': 366,
      '/latest': 1
    }
    const qpath = mymap[location.pathname]; // returns 0 for / and 7 for week...
    const fetchArticles = async () => {
      try {
        setFailure(false);
        setPosts([]);
        //
        const url = 'https://dev.to/api/articles' + (qpath ? '?top=' + qpath : '');
        const api_response = await fetch(url);
        const data = await api_response.json();

        if (api_response.status !== 200) {
          throw Error(api_response.error);
        }
        // console.log(data);
        setQuery(location.pathname); // update this after a successful API request
        setPosts(data);
      } catch (error) {
        // console.log(error);
        setFailure(true);
        setQuery(''); // do this to allow new request without change in location
      }
    }
    !isNaN(qpath) && fetchArticles();
  }, [location]) // the effect hook will only run when there is a change in the location's pathname, or after a failed request

  const navState = {SideNavToggler, isShowing};
  const data = {query, failure, posts, handleChange, ...navState};

  return (
    <div className="App">
      {/* <Switch>
        <Route path="/" render={() => <Home data={data} />} exact />
        <Route path="/top" render={() => <Home data={data} />} />
        <Route path="/latest" render={() => <Home data={data} />} />
        <Route path="/search" component={Search} />

        <Route render={() => <Article data={navState} />} />
      </Switch> */}
    </div>
  );
}

export default App;
```
With this controller, if a user clicks on the `nav` link, the location will be updated, and since we have added the location as a dependency to the useEffet hook, we are confident that a new request would be made to the API backend and the UI will re-render upon successful request.

If you wish to learn more about the useState hook, you can [read my article](https://dev.to/maswerdna/react-usereducer-for-dummies-35pc) on that where I demonstrated the use of `useState` and `useReducer` hooks.

### Summary
In this first 2 sections of this part, we have learnt how to create a new react app and how to use the `useEffect` hook to make asynchronous requests to the server. We also learnt how to use the `useState` hook to manage the internal state of our app.

We saw how we can update the browser history to activate a server request by using the React Router package with the useEffect hook and we also looked at using media queries to set break-points in our app for responsive design.


### What's next?
In Part 2 of this article, we will take a dive into the world of React Router for SPA navigation and how to configure our app to be compliant with github pages mode of navigation.

If you like this article and would love to be notified when the next update is ready, you can add it to your reading list by clicking the `Save` button or you can as well follow my account.

Thank you ;)

[Link to Source Code on GitHub](https://github.com/maswerdna/dev-to-blog)