Finishing my project ( n°5 )
Hello again
Today I went mega coding session and finished my first react project!
I decided that I would make the app display data from a bunch of API's.
To do this i created this component called Widget
that takes in a params
object in the props. This object has all the necessary information to fetch data from a specific API and then display it correctly :
{
url: "https://example.com/api/",
propertyName: "data",
subPropertyName: "data"
title: "Amazing data",
borderColor: "tailwindcss-color",
type: "reload/no reload"
}
So lets brake down this object:
url
This option is followed by the URL of the API endpointpropertyName
This is used to access the right part of data returned from the request and does this thanks to object manipulation methods:setContent( data[propertyName] )
subPropertyName
this is an optional argument, and is used if the desired data is nested further in the response object.title
This is the title of the widget, displayed above the databorderColor
you can set this property to the indended border color of the widget (uses tailwind classes.type
This property can have 2 options:- if
type
is set to"reload"
then a button will appear next to the data letting you send a new request to the API to fetch different data. - if
type
is set to"no reload"
, there will not be a reload button. this option is used for API's that will always send the same data.
- if
Now let's break down my widget component:
import React from 'react'
import { useState ,useEffect } from 'react';
import NextButton from './NextButton';
First of all I import React and various hooks, then a custom button i made.
Next, I initialize the component and use object de-structuring to extract variables from the params
prop.
export default function Widget(props) {
const { url, propertyName, title, borderColor, type } = props.params;
After this, I create 2 states, the content state to store the widgets content, and the needsReload state, which will later be used to send new requests.
const [content, setContent] = useState(null);
const [needsReload, setNeedsReload] = useState(false);
The next part creates a subProperty
variable if the later exists in the params object. I used error handling for this but their might be a better solution.
var subPropertyName;
try {
subPropertyName = props.params.subPropertyName;
} catch(err) {
subPropertyName = false
}
A useEffect
hook is then used to fecth the data from the API. The content state is then set to the meaningful part of the data. This is where the subProperty
option comes in handy in case the data is nested deeper inside the response.
useEffect(() => {
fetch(url)
.then(response => response.json())
.then(data => {
if (subPropertyName) {
setContent(data[propertyName][subPropertyName])
} else {
setContent(data[propertyName])
}
})
.catch(err => setContent('there was an error'))
}, [needsReload])
needsReload
is passed into the effect to trigger it again if it is updated, creating an easy way to send new requests if the variable is changed.
Fetching the API itself uses .then()
to handle the promise and .catch()
to handle errors.
Next I use a template literal to for dynamic styling of the widget, i can pass in the color from the params
object.
const contentClass = `widget-content border-${borderColor}`
Finally, I return the widget with a title and the data, and conditional rendering is used to include (or not) the reload button.
if (type == "reload") {
return (
<div className="wiget-container">
<div
className={contentClass}
>
<div>
<h1 className="font-bold text-2xl">{title}</h1>
<p className="font-semibold">{content}</p>
</div>
</div>
<NextButton setNeedsReload={setNeedsReload} needsReload={needsReload} borderColor={borderColor} />
</div>
)
} else {
return (
<div className="wiget-container">
<div
className={contentClass}
>
<div>
<h1 className="font-bold text-2xl">{title}</h1>
<p className="font-semibold">{content}</p>
</div>
</div>
</div>
)
}
}
Here is the full component:
import React from 'react'
import { useState ,useEffect } from 'react';
import NextButton from './NextButton';
export default function Widget(props) {
const { url, propertyName, title, borderColor, type } = props.params;
const [content, setContent] = useState(null);
const [needsReload, setNeedsReload] = useState(false);
var subPropertyName;
try {
subPropertyName = props.params.subPropertyName;
} catch(err) {
subPropertyName = false
}
useEffect(() => {
fetch(url)
.then(response => response.json())
.then(data => {
if (subPropertyName) {
setContent(data[propertyName][subPropertyName])
} else {
setContent(data[propertyName])
}
})
.catch(err => setContent('there was an error'))
}, [needsReload])
//template literal for dynamic styling
const contentClass = `widget-content border-${borderColor}`
if (type == "reload") {
return (
<div className="wiget-container">
<div
className={contentClass}
>
<div>
<h1 className="font-bold text-2xl">{title}</h1>
<p className="font-semibold">{content}</p>
</div>
</div>
<NextButton setNeedsReload={setNeedsReload} needsReload={needsReload} borderColor={borderColor} />
</div>
)
} else {
return (
<div className="wiget-container">
<div
className={contentClass}
>
<div>
<h1 className="font-bold text-2xl">{title}</h1>
<p className="font-semibold">{content}</p>
</div>
</div>
</div>
)
}
}
Thanks to this component i can map a bunch of API params
to display data from as many API's as nescesarry!
export default function Home() {
const APIlist = [
{
url: "https://v2.jokeapi.dev/joke/Any?safe-mode&type=single",
propertyName: "joke",
title: "Joke:",
borderColor: "th-blue-cyan",
type: "reload"
},
{
url: "https://www.boredapi.com/api/activity",
propertyName: "activity",
title: "Are you bored? Try this:",
borderColor: "th-pink-true",
type: "reload"
},
{
url: "https://api.ipify.org/?format=json",
propertyName: "ip",
title: "your IP:",
borderColor: "th-yellow-gold",
type: "no reload"
},
{
url: "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd",
propertyName: "bitcoin",
subPropertyName:"usd",
title: "btc price ($usd):",
borderColor: "th-red-chili",
type: "no reload"
},
{
url: "https://v2.jokeapi.dev/joke/programming?safe-mode&type=single",
propertyName: "joke",
title: "Programming joke:",
borderColor: "th-blue-cyan",
type: "reload"
},
]
return (
APIlist.map(api => {
return <Widget key={api.title} params={api} />
})
)
}
Here are what those widgets look like:
So this will conclude my learning react Blog series, I have really enjoyed using react and think it's a great way to work with JavaScript in frontend development!