Hook Dev.to blogs to your react app
Tags: react, javascript, beginners, tutorial
Published: 2021-10-13 02:23:54


So, I love the Dev.to community and more so I love how easy it is to create a blog. So, I was looking for a way to have my blogs that I publish on here go straight to my personal site. Thankfully, this was pretty easy due to dev's [api](https://developers.forem.com/api) and a couple react hooks. Here is how I implemented mine on my [personal site](www.simplymincy.info).

### Contents
 1. Direct to a specific blog article
 2. List your blog articles on a page
 3. Dynamically go to a blog article from a list
 
**In a rush? Here is the [Github Repo](https://github.com/ajmincy/devBlogToReact)**


For the sake of time I am going to make a few assumptions here:

> You are familiar with React and React Hooks
> You have a basic understanding of an API
> You have at least one published article on Dev.to

## 1. Direct to a specific blog article
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u677gxd16j8akakr4gty.jpg)


Let's start with creating a basic react app:  `npx create-react-app dev-blog`   
Once the app is done installing let's do our usual boilerplate clean up of the `App.js` file. Here is mine:

    function App() {
    
	    return (
		    <div>
			    Dev Blog
		    </div>
	    );
    }
    
    export  default App;

Now let's take a look at the api docs [here](https://developers.forem.com/api). While their api is still in beta, as you can see there are several ways you can retrieve data from the site. As you explore different paths and responses you may come across a few that are not correct or not returning but I have yet to come across one and overall am pretty pleased with their work. 

The path I recommend is under `articles` and named the *A published article by path* [`GET` ](https://developers.forem.com/api#operation/getArticleByPath) call. Let's add a constant named blogPath to our code and we want to set that const to `https://dev.to/api/articles/{Your UserName}/{Your Article ID}` Simply navigate to a blog post you want show to get that Article ID. Here is mine. 

    function App() {
    const blogPath = 'https://dev.to/api/articles/simplymincy/test-post-2-2j8a`';
	    return (
		    <div>
			    Dev Blog
		    </div>
	    );
    }
    
    export  default App;

The next thing we want to do is make a call with that const to get the response body. To do this we can use `useState` and `useEffect` and a package commonly used with react called `axios`, which will be used to make our http requests.  Let's go ahead and install that package `npm i axios`. 

After that installs let's use the useState hook and set it to be our article Body response.

    import React, { useEffect, useState } from  "react";
    import axios from  "axios";
     
    function App() {
    const blogPath = 'https://dev.to/api/articles/simplymincy/test-post-2-2j8a`';
    const [articleBody, setArticleBody] = useState();

	    return (
		    <div>
			    Dev Blog
		    </div>
	    );
    }
    
    export  default App;

To actually get that response we will need to now use useEffect and axios to make that call using the blogPath we created. It should look something like I have below with us checking for the response.data and setting that to our setArticleBody. Be sure to set a dependency here in your useEffect or this call will continuously run in the background. We only want this to update if there is a page load so we can set it dependent to the blogPath.

> Axios makes it really easy to make requests. Feel free to check out their [docs](https://axios-http.com/docs/intro)

    import React, { useEffect, useState } from  "react";
    import axios from  "axios";
    
    function App() {
    const blogPath = 'https://dev.to/api/articles/simplymincy/test-post-2-2j8a`';
    
    const [articleBody, setArticleBody] = useState();
    
	    useEffect(() => {
		    axios.get(blogPath).then((response) => {
			    setArticleBody(response.data);
		    });
	    }, [blogPath]);
	    
	    return (
		    <div>
			    Dev Blog
		    </div>
	    );
    }
    
    export  default App;

Now at this point you would think we could replace the contents in the return with one of the properties we are getting in our response. I mean for sure "*body_html*" property should work right? Just use that and place that in the return. I am sure you know the problem with trying that but if you don't it is because that return is not real `HTML`. It is `jsx`. So, it will not do any formatting. So, instead let's go with the "*body_markdown*" property instead and to get the markdown formatting to show up correctly on our page we are going to use another package `react-markdown`. This package converts your markdown code into code `jsx` can understand and displays it properly. So, let's install that package and make those updates shall we:

    function App() {
    const blogPath = 'https://dev.to/api/articles/simplymincy/test-post-2-2j8a`';
    
    const [articleBody, setArticleBody] = useState();
    
	    useEffect(() => {
		    axios.get(blogPath).then((response) => {
			    setArticleBody(response.data);
		    });
	    }, [blogPath]);
	    
	    if (!articleBody) return  null;
	    
	    const markdown = articleBody.body_markdown;

	    return (
		    <div>
			    <ReactMarkdown>{markdown}</ReactMarkdown>
		    </div>
	    );
    }
    
    export  default App;

I've also added a simple check on the body response to see if it exists and to return null if it doesn't. That way our app will not crash if that path no longer exists.

At this point you should be able to see the page loaded with your article. This is pretty basic but we can do some more dynamic things with this api.

## 2. List a collection of articles on a page
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/67jyvmfu34bz7ahne7u9.jpg)


So, first thing we want to do here is make a new component. Let's call it `Article.js` because this is where we will return... well our articles. 

     const Article = () => {
    		return ();
     };
     
     export  default Article;

And since we did all that work already in our `App.js` file. Let's just move that over to this `Article.js` file. 


    import React, { useEffect, useState } from  "react";
    import axios from  "axios";
    import ReactMarkdown from  "react-markdown";
    
    const Article = () => {
    
    const blogPath = `https://dev.to/api/articles/simplymincy/test-post-2-2j8a`;
    const [articleBody, setArticleBody] = useState();
    
	    useEffect(() => {
		    axios.get(blogPath).then((response) => {
			    setArticleBody(response.data);
			});  
	    }, [blogPath]);

	    if (!articleBody) return  null;

    const markdown = articleBody.body_markdown;

	    return (  
		    <div> Dev Blog
			    <ReactMarkdown>{markdown}</ReactMarkdown>
		    </div>
	    );
    };
    
    export  default Article;

Our `App.js` should be empty now and look like this:

    import React from  "react";

    function App() {
    
    return (
    <div>
    Empty App
    </div>
    
    );
    
    }

    export  default App;

Before we do anything else to that file let's create our collection component `Collection.js` with some of the same imports as before:

    import React, { useEffect, useState } from  "react";
    import axios from  "axios";
    
    const Collection = () => {

    return ();    
    };
    export  default Collection;

And we want to retrieve the data similar to how we did with the Article component but we want to use this url `https://dev.to/api/articles?username={Your UserName}` to get the collection of articles instead.

    import React, { useEffect, useState } from  "react";
    import axios from  "axios";
    
    const Collection = () => {
	    const collectionPath = `https://dev.to/api/articles?username=simplymincy`;
	    const [articleCollection, setArticleCollection] = useState();
    
	    useEffect(() => {
		    axios.get(collectionPath).then((response) => {
			    setArticleCollection(response.data);
		    });
	    }, [collectionPath]);
    
	    if (!articleCollection) return  null;
    
	    return  <div>Dev Collection</div>;
    };
    
    export  default Collection;

Since we will be getting an array of data we will use the `map()` method to display every element. It should look something like I have below where we are taking that `articleCollection` and spitting out every `article` (You can name that whatever you like) based on its `id`.

    const list = articleCollection.map((article) => (
    
	    <div  key={article.id}>
			Data we want to display here
	    </div>
    ));
	    return (
		    <div>Dev Collection
			    {list}
		    </div>
	    );

I also want to display an image and the title of the article so I will add a couple more but feel free to add whatever you like here based off the api.

    import React, { useEffect, useState } from  "react";
    import axios from  "axios";

    const Collection = () => {
	    const collectionPath = `https://dev.to/api/articles?username=simplymincy`;
		const [articleCollection, setArticleCollection] = useState();
			 
			useEffect(() => {
			    axios.get(collectionPath).then((response) => {
				    setArticleCollection(response.data);
			    });
			}, [collectionPath]);

	    if (!articleCollection) return  null;
		const list = articleCollection.map((article) => (
    
		    <div  key={article.id}>
			    <div>
				    <div>
					    <img  src={article.social_image}  alt="Article Images"  />
				    </div>
					    <div>
						    <h4>{article.title}</h4>
					    </div>
				    </div>
			    </div>
		    ));
    
	    return (
    
		    <div>
			    Dev Collection
			    {list}
		    </div>
	    );
    };
    
    export  default Collection;
Let's now import the Collection component in our `App.js` file

    import React from  "react";
    import Collection from  "./Collection";

    function App() {
    
	    return (
			<React.Fragment>
			    <Collection/>
		    </React.Fragment>
		);
    }

    export  default App;
We should now see a collection of our published articles on our page. Now the last thing we want to do (besides adding some css to have it more appeasing on our eyes) is have each article clickable and navigate to that particular article onClick. Let's do that now. 

## 3. Navigate to a blog article from a list
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/snuf968mhn0ua2u3qq3v.jpg)


For this we are going to use another common package that is used with React and that is `react-router-dom`. So, let's install that and before we get the `Collection` component ready to have a clickable element we need to go to our `App.js` file and make a few changes. Right now our app is a single page app. Being that we will at the very least now have 2 pages we can now navigate, we need to set up some routes. This is a decent enough change so for this one I am going to show the fully updated `App.js` file and then explain.

```
import React from "react";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import Article from "./Article";
import Collection from "./Collection";

function App() {
  return (
    <React.Fragment>
      <BrowserRouter basename={"/"}>
        <Switch>
          <Route
            exact
            path={`${process.env.PUBLIC_URL}/`}
            component={Collection}
          />
          <Route
            exact
            path={`${process.env.PUBLIC_URL}/Article/simplymincy/:path`}
            component={Article}
          />
        </Switch>
      </BrowserRouter>
    </React.Fragment>
  );
}

export default App;

```

I am now using the `react-router-dom` package in our most top level component because I want to set up the different routes I spoke of before. Hence why we are using the `BrowserRouter`, `Switch`, and `Route` methods. The `BrowserRouter` tag is used to push, replace, and pop the various return states we will now have in our app. Inside of that we set a `Switch` tag which works very similiar to a switch statement meaning that it returns the first child it has that matches a location and as you can see we have 2 `Route` tags. There are a lot of properties you can set for these and I highly recommend looking over the docs [here](https://reactrouter.com/web/guides/quick-start), but for my example I am checking for an exact match on the path property and I am setting the components to the 2 components we created earlier. Looking closer you will see that I have the `Collection` component set as my base url and the route with the `Article` component with a more unique path. The `/Article/simplymincy/` was simply what I chose here but you can have your path be anything as long as it is unique. 

Last thing, I want to point out here is the `:path` piece you see in that url. We use this here so that when we do eventually click on a link in our `Collection` component we will pass it some parameters. We do that by importing `react-router-dom` to our component and using the `Link` method. Think of this as an `<a>` tag. Here is the updated file now:

```
    import React, { useEffect, useState } from  "react";
    import axios from  "axios";
    import { Link } from  "react-router-dom";

    const Collection = () => {
	    const collectionPath = `https://dev.to/api/articles?username=simplymincy`;

	    const [articleCollection, setArticleCollection] = useState();
    
	    useEffect(() => {
    
		    axios.get(collectionPath).then((response) => {
			    setArticleCollection(response.data);
		    });
	    }, [collectionPath]);

	    if (!articleCollection) return  null;

	    const list = articleCollection.map((article) => (
    
		    <div  key={article.id}>
			    <div>
				    <div>
					    <img  src={article.social_image}  alt="Article Images"  />
				    </div>
					<div>
					    <h4>{article.title}</h4>
				    </div>
			    </div>
			    <div>
				    <Link  to={`/Article/simplymincy/${article.slug}`}>Read</Link>
			    </div>
		    </div>
	    ));

    return (
    
	    <div>
    
		    Dev Collection
			    {list}
	    </div>
	    );
	};

    export  default Collection;
```
As you will notice in the `Link` tag we are using the same url that we set in the Article route in the `App.js` file. That is key, because remember we are using exact match here. If they do not match it will not return our `Article` component. Which leads us to our last step. Remember when we set the `:path` in our `Article` route well we did that so we can set some value and pass it through our components freely. In this case we are going to use a value we get from our api call, which is `slug`. This by the definition of the dev.to api is used to uniquely identify the articles users create. This is not only perfect to have our urls be unique but also will be able to get passed through so our `Article` component can make a direct call on that path to gets its blog content. This update is rather small. We just want to `import { useParams } from  "react-router-dom";` and then replace our blogPath const with this:

```
    const params = useParams();
    const blogPath = `https://dev.to/api/articles/simplymincy/${params.path}`;
```
What we are doing here is passing in the parameters from the link we clicked on in the Collection component and we will set that slug to be the end of our blogPath. That way no matter what article we click on, it will know which one to retrieve its content for and display.

AND THATS IT! 

At this point you should have all of your articles in a collection displaying and be able to click and display each article dynamically. With the API you can do a lot more such as GET and POST comments, likes, and other useful stats pertaining to your blog. I did not add any styling in here either. I leave that up to you. Here is an example of how I implemented mine.
> From my personal site
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/94v9u8z5c8ch1f9chs2b.png)