@ -0,0 +1,5 @@
|
||||
# 30 Days of React App: Day 19
|
||||
|
||||
In the project directory, you can run to start the project
|
||||
|
||||
### `npm start`
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
@ -0,0 +1,84 @@
|
||||
import React, { Component } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import axios from 'axios'
|
||||
|
||||
const Country = ({
|
||||
country: { name, capital, flag, languages, population, currency },
|
||||
}) => {
|
||||
const formatedCapital =
|
||||
capital.length > 0 ? (
|
||||
<>
|
||||
<span>Capital: </span>
|
||||
{capital}
|
||||
</>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
const formatLanguage = languages.length > 1 ? `Languages` : `Language`
|
||||
console.log(languages)
|
||||
return (
|
||||
<div className='country'>
|
||||
<div className='country_flag'>
|
||||
<img src={flag} alt={name} />
|
||||
</div>
|
||||
<h3 className='country_name'>{name.toUpperCase()}</h3>
|
||||
<div class='country_text'>
|
||||
<p>{formatedCapital}</p>
|
||||
<p>
|
||||
<span>{formatLanguage}: </span>
|
||||
{languages.map((language) => language.name).join(', ')}
|
||||
</p>
|
||||
<p>
|
||||
<span>Population: </span>
|
||||
{population.toLocaleString()}
|
||||
</p>
|
||||
<p>
|
||||
<span>Currency: </span>
|
||||
{currency}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
class App extends Component {
|
||||
state = {
|
||||
data: [],
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchCountryData()
|
||||
}
|
||||
fetchCountryData = async () => {
|
||||
const url = 'https://restcountries.eu/rest/v2/all'
|
||||
try {
|
||||
const response = await axios.get(url)
|
||||
const data = await response.data
|
||||
this.setState({
|
||||
data,
|
||||
})
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='App'>
|
||||
<h1>React Component Life Cycle</h1>
|
||||
<h1>Calling API</h1>
|
||||
<div>
|
||||
<p>There are {this.state.data.length} countries in the api</p>
|
||||
<div className='countries-wrapper'>
|
||||
{this.state.data.map((country) => (
|
||||
<Country country={country} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
@ -0,0 +1,496 @@
|
||||
<div align="center">
|
||||
<h1> 30 Days Of React: Fetch and Axios</h1>
|
||||
<a class="header-badge" target="_blank" href="https://www.linkedin.com/in/asabeneh/">
|
||||
<img src="https://img.shields.io/badge/style--5eba00.svg?label=LinkedIn&logo=linkedin&style=social">
|
||||
</a>
|
||||
<a class="header-badge" target="_blank" href="https://twitter.com/Asabeneh">
|
||||
<img alt="Twitter Follow" src="https://img.shields.io/twitter/follow/asabeneh?style=social">
|
||||
</a>
|
||||
|
||||
<sub>Author:
|
||||
<a href="https://www.linkedin.com/in/asabeneh/" target="_blank">Asabeneh Yetayeh</a><br>
|
||||
<small> October, 2020</small>
|
||||
</sub>
|
||||
|
||||
</div>
|
||||
|
||||
[<< Day 17](../17_React_Router/17_react_router.md) | [Day 19>>]()
|
||||
|
||||
![30 Days of React banner](../images/30_days_of_react_banner_day_18.jpg)
|
||||
|
||||
- [Fetch and Axios](#fetch-and-axios)
|
||||
- [Fetch](#fetch)
|
||||
- [Axios](#axios)
|
||||
- [Exercises](#exercises)
|
||||
- [Exercises: Level 1](#exercises-level-1)
|
||||
- [Exercises: Level 1](#exercises-level-1-1)
|
||||
|
||||
# Fetch and Axios
|
||||
|
||||
## Fetch
|
||||
|
||||
Currently, JavaScript provides a fetch API to make HTTP requests. Fetch might not be supported by all browsers therefore we have install addition package for browser supports. However, if we use Axios we do not need to use additional package for browser support. Axios code seems neater than fetch. In this section we will see the difference between fetch and axios. May be if you want to know the browser support of fetch you check out on [caniuse](https://caniuse.com/ciu/index) website. As of today, it has 95.62% browser support.
|
||||
|
||||
```js
|
||||
import React, { Component } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
const Country = ({
|
||||
country: { name, capital, flag, languages, population, currency },
|
||||
}) => {
|
||||
const formatedCapital =
|
||||
capital.length > 0 ? (
|
||||
<>
|
||||
<span>Capital: </span>
|
||||
{capital}
|
||||
</>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
const formatLanguage = languages.length > 1 ? `Languages` : `Language`
|
||||
console.log(languages)
|
||||
return (
|
||||
<div className='country'>
|
||||
<div className='country_flag'>
|
||||
<img src={flag} alt={name} />
|
||||
</div>
|
||||
<h3 className='country_name'>{name.toUpperCase()}</h3>
|
||||
<div class='country_text'>
|
||||
<p>{formatedCapital}</p>
|
||||
<p>
|
||||
<span>{formatLanguage}: </span>
|
||||
{languages.map((language) => language.name).join(', ')}
|
||||
</p>
|
||||
<p>
|
||||
<span>Population: </span>
|
||||
{population.toLocaleString()}
|
||||
</p>
|
||||
<p>
|
||||
<span>Currency: </span>
|
||||
{currency}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
class App extends Component {
|
||||
state = {
|
||||
data: [],
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const url = 'https://restcountries.eu/rest/v2/all'
|
||||
fetch(url)
|
||||
.then((response) => {
|
||||
return response.json()
|
||||
})
|
||||
.then((data) => {
|
||||
console.log(data)
|
||||
this.setState({
|
||||
data,
|
||||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='App'>
|
||||
<h1>React Component Life Cycle</h1>
|
||||
<h1>Calling API</h1>
|
||||
<div>
|
||||
<p>There are {this.state.data.length} countries in the api</p>
|
||||
<div className='countries-wrapper'>
|
||||
{this.state.data.map((country) => (
|
||||
<Country country={country} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
We can implement async and await and make the above code short and clean.
|
||||
|
||||
```js
|
||||
import React, { Component } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
const Country = ({
|
||||
country: { name, capital, flag, languages, population, currency },
|
||||
}) => {
|
||||
const formatedCapital =
|
||||
capital.length > 0 ? (
|
||||
<>
|
||||
<span>Capital: </span>
|
||||
{capital}
|
||||
</>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
const formatLanguage = languages.length > 1 ? `Languages` : `Language`
|
||||
console.log(languages)
|
||||
return (
|
||||
<div className='country'>
|
||||
<div className='country_flag'>
|
||||
<img src={flag} alt={name} />
|
||||
</div>
|
||||
<h3 className='country_name'>{name.toUpperCase()}</h3>
|
||||
<div class='country_text'>
|
||||
<p>{formatedCapital}</p>
|
||||
<p>
|
||||
<span>{formatLanguage}: </span>
|
||||
{languages.map((language) => language.name).join(', ')}
|
||||
</p>
|
||||
<p>
|
||||
<span>Population: </span>
|
||||
{population.toLocaleString()}
|
||||
</p>
|
||||
<p>
|
||||
<span>Currency: </span>
|
||||
{currency}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
class App extends Component {
|
||||
state = {
|
||||
data: [],
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchCountryData()
|
||||
}
|
||||
|
||||
fetchCountryData = async () => {
|
||||
const url = 'https://restcountries.eu/rest/v2/all'
|
||||
const response = await fetch(url)
|
||||
const data = await response.json()
|
||||
this.setState({
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='App'>
|
||||
<h1>Fetching API using Fetch</h1>
|
||||
<h1>Calling API</h1>
|
||||
<div>
|
||||
<p>There are {this.state.data.length} countries in the api</p>
|
||||
<div className='countries-wrapper'>
|
||||
{this.state.data.map((country) => (
|
||||
<Country country={country} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
If we use async and await we handle the error using try and catch. Let's apply a try catch block in the above code.
|
||||
|
||||
```js
|
||||
import React, { Component } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
const Country = ({
|
||||
country: { name, capital, flag, languages, population, currency },
|
||||
}) => {
|
||||
const formatedCapital =
|
||||
capital.length > 0 ? (
|
||||
<>
|
||||
<span>Capital: </span>
|
||||
{capital}
|
||||
</>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
const formatLanguage = languages.length > 1 ? `Languages` : `Language`
|
||||
console.log(languages)
|
||||
return (
|
||||
<div className='country'>
|
||||
<div className='country_flag'>
|
||||
<img src={flag} alt={name} />
|
||||
</div>
|
||||
<h3 className='country_name'>{name.toUpperCase()}</h3>
|
||||
<div class='country_text'>
|
||||
<p>{formatedCapital}</p>
|
||||
<p>
|
||||
<span>{formatLanguage}: </span>
|
||||
{languages.map((language) => language.name).join(', ')}
|
||||
</p>
|
||||
<p>
|
||||
<span>Population: </span>
|
||||
{population.toLocaleString()}
|
||||
</p>
|
||||
<p>
|
||||
<span>Currency: </span>
|
||||
{currency}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
class App extends Component {
|
||||
state = {
|
||||
data: [],
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchCountryData()
|
||||
}
|
||||
|
||||
fetchCountryData = async () => {
|
||||
const url = 'https://restcountries.eu/rest/v2/all'
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
const data = await response.json()
|
||||
this.setState({
|
||||
data,
|
||||
})
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='App'>
|
||||
<h1>Fetching API using Fetch</h1>
|
||||
<h1>Calling API</h1>
|
||||
<div>
|
||||
<p>There are {this.state.data.length} countries in the api</p>
|
||||
<div className='countries-wrapper'>
|
||||
{this.state.data.map((country) => (
|
||||
<Country country={country} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
Now, let's see how to do the same API call using axios.
|
||||
|
||||
How can do fetch if we have multiple API two call ?
|
||||
|
||||
## Axios
|
||||
|
||||
Axios is a third party package and we need to install it using npm. It is the most popular way to make HTTP requests(GET, POST, PUT, PATCH, DELETE). In this example, we will cover only a get request.
|
||||
|
||||
```js
|
||||
import React, { Component } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import axios from 'axios'
|
||||
|
||||
const Country = ({
|
||||
country: { name, capital, flag, languages, population, currency },
|
||||
}) => {
|
||||
const formatedCapital =
|
||||
capital.length > 0 ? (
|
||||
<>
|
||||
<span>Capital: </span>
|
||||
{capital}
|
||||
</>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
const formatLanguage = languages.length > 1 ? `Languages` : `Language`
|
||||
console.log(languages)
|
||||
return (
|
||||
<div className='country'>
|
||||
<div className='country_flag'>
|
||||
<img src={flag} alt={name} />
|
||||
</div>
|
||||
<h3 className='country_name'>{name.toUpperCase()}</h3>
|
||||
<div class='country_text'>
|
||||
<p>{formatedCapital}</p>
|
||||
<p>
|
||||
<span>{formatLanguage}: </span>
|
||||
{languages.map((language) => language.name).join(', ')}
|
||||
</p>
|
||||
<p>
|
||||
<span>Population: </span>
|
||||
{population.toLocaleString()}
|
||||
</p>
|
||||
<p>
|
||||
<span>Currency: </span>
|
||||
{currency}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
class App extends Component {
|
||||
state = {
|
||||
data: [],
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const url = 'https://restcountries.eu/rest/v2/all'
|
||||
axios
|
||||
.get(url)
|
||||
.then((response) => {
|
||||
this.setState({
|
||||
data: response.data,
|
||||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='App'>
|
||||
<h1>React Component Life Cycle</h1>
|
||||
<h1>Calling API</h1>
|
||||
<div>
|
||||
<p>There are {this.state.data.length} countries in the api</p>
|
||||
<div className='countries-wrapper'>
|
||||
{this.state.data.map((country) => (
|
||||
<Country country={country} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
Let's also implement the axios fetching using async and await.
|
||||
|
||||
```js
|
||||
import React, { Component } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import axios from 'axios'
|
||||
|
||||
const Country = ({
|
||||
country: { name, capital, flag, languages, population, currency },
|
||||
}) => {
|
||||
const formatedCapital =
|
||||
capital.length > 0 ? (
|
||||
<>
|
||||
<span>Capital: </span>
|
||||
{capital}
|
||||
</>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
const formatLanguage = languages.length > 1 ? `Languages` : `Language`
|
||||
console.log(languages)
|
||||
return (
|
||||
<div className='country'>
|
||||
<div className='country_flag'>
|
||||
<img src={flag} alt={name} />
|
||||
</div>
|
||||
<h3 className='country_name'>{name.toUpperCase()}</h3>
|
||||
<div class='country_text'>
|
||||
<p>{formatedCapital}</p>
|
||||
<p>
|
||||
<span>{formatLanguage}: </span>
|
||||
{languages.map((language) => language.name).join(', ')}
|
||||
</p>
|
||||
<p>
|
||||
<span>Population: </span>
|
||||
{population.toLocaleString()}
|
||||
</p>
|
||||
<p>
|
||||
<span>Currency: </span>
|
||||
{currency}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
class App extends Component {
|
||||
state = {
|
||||
data: [],
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchCountryData()
|
||||
}
|
||||
fetchCountryData = async () => {
|
||||
const url = 'https://restcountries.eu/rest/v2/all'
|
||||
try {
|
||||
const response = await axios.get(url)
|
||||
const data = await response.data
|
||||
this.setState({
|
||||
data,
|
||||
})
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='App'>
|
||||
<h1>React Component Life Cycle</h1>
|
||||
<h1>Calling API</h1>
|
||||
<div>
|
||||
<p>There are {this.state.data.length} countries in the api</p>
|
||||
<div className='countries-wrapper'>
|
||||
{this.state.data.map((country) => (
|
||||
<Country country={country} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
As you have seen, there is no much difference between fetch and axios. But I recommend you to use more axios than fetch because of browser support and easy of use.
|
||||
|
||||
# Exercises
|
||||
|
||||
## Exercises: Level 1
|
||||
|
||||
1. What is HTTP request?
|
||||
2. What are the most common HTTP requests?
|
||||
3. What is fetch?
|
||||
4. What is axios?
|
||||
5. What is the difference between fetch and axios?
|
||||
6. Do you prefer fetch to axios for make HTTP requests?
|
||||
|
||||
## Exercises: Level 1
|
||||
|
||||
1. Find the average metric weight and life span of cats in the following [API](https://api.thecatapi.com/v1/breeds). There are 67 breeds of cats in API.
|
||||
|
||||
🎉 CONGRATULATIONS ! 🎉
|
||||
|
||||
[<< Day 17](../17_React_Router/17_react_router.md) | [Day 19>>]()
|
@ -1,303 +0,0 @@
|
||||
import React, { Component } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import {
|
||||
BrowserRouter as Router,
|
||||
Route,
|
||||
Switch,
|
||||
NavLink,
|
||||
Redirect,
|
||||
Prompt,
|
||||
} from 'react-router-dom'
|
||||
|
||||
// Home component
|
||||
const Home = (props) => <h1>Welcome Home</h1>
|
||||
// About component
|
||||
const About = (props) => <h1>About Us</h1>
|
||||
// Contact component
|
||||
const Contact = (props) => <h1>Contact us</h1>
|
||||
// Challenge component
|
||||
|
||||
const challenges = [
|
||||
{
|
||||
name: '30 Days Of Python',
|
||||
description:
|
||||
'30 Days of Python challenge is a step by step guide to learn Python in 30 days.',
|
||||
status: 'completed',
|
||||
days: 30,
|
||||
level: 'Beginners to Advanced',
|
||||
duration: '20 Nov 2019 - 20 Dec 2019',
|
||||
slug: 'pyhton',
|
||||
url:
|
||||
'https://github.com/https://https://github.com/Asabeneh/30-Days-Of-Python.com/Asabeneh/30-Days-Of-JavaScript/30-Days-Of-React',
|
||||
author: {
|
||||
firstName: 'Asabeneh',
|
||||
lastName: 'Yetayeh',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '30 Days Of JavaScript',
|
||||
description:
|
||||
'30 Days of JavaScript challenge is a step by step guide to learn JavaScript in 30 days.',
|
||||
status: 'completed',
|
||||
days: 30,
|
||||
level: 'Beginners to Advanced',
|
||||
duration: '1 Jan 2020 - 30 Jan 2020',
|
||||
slug: 'javascript',
|
||||
url: 'https://github.com/Asabeneh/30-Days-Of-JavaScript',
|
||||
author: {
|
||||
firstName: 'Asabeneh',
|
||||
lastName: 'Yetayeh',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '30 Days Of React',
|
||||
description:
|
||||
'30 Days of React challenge is a step by step guide to learn React in 30 days.',
|
||||
status: 'ongoing',
|
||||
days: 30,
|
||||
level: 'Beginners to Advanced',
|
||||
duration: '1 Oct 2020- 30 Oct 2020',
|
||||
slug: 'react',
|
||||
url: 'https://github.com/Asabeneh/30-Days-Of-React',
|
||||
author: {
|
||||
firstName: 'Asabeneh',
|
||||
lastName: 'Yetayeh',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '30 HTML and CSS',
|
||||
description:
|
||||
'30 Days of HTML and CSS challenge is a step by step guide to learn HTML and CSS in 30 days.',
|
||||
|
||||
status: 'coming',
|
||||
days: 30,
|
||||
level: 'Beginners to Advanced',
|
||||
duration: '',
|
||||
slug: 'html-and-css',
|
||||
url: '',
|
||||
author: {
|
||||
firstName: 'Asabeneh',
|
||||
lastName: 'Yetayeh',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '30 ReactNative',
|
||||
description:
|
||||
'30 Days of ReactNative challenge is a step by step guide to learn ReactNative in 30 days.',
|
||||
status: 'coming',
|
||||
days: 30,
|
||||
level: 'Beginners to Advanced',
|
||||
duration: '',
|
||||
slug: 'reactnative',
|
||||
url: '',
|
||||
author: {
|
||||
firstName: 'Asabeneh',
|
||||
lastName: 'Yetayeh',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '30 Data Analysis',
|
||||
description:
|
||||
'30 Days of Data Analysis challenge is a step by step guide to learn about data, data visualization and data analysis in 30 days.',
|
||||
status: 'coming',
|
||||
days: 30,
|
||||
level: 'Beginners to Advanced',
|
||||
duration: '',
|
||||
slug: 'data-analysis',
|
||||
url: '',
|
||||
author: {
|
||||
firstName: 'Asabeneh',
|
||||
lastName: 'Yetayeh',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '30 Machine Learning',
|
||||
description:
|
||||
'30 Days of Machine learning challenge is a step by step guide to learn data cleaning, machine learning models and predictions in 30 days.',
|
||||
status: 'coming',
|
||||
days: 30,
|
||||
level: 'Beginners to Advanced',
|
||||
duration: '',
|
||||
slug: 'machine-learning',
|
||||
url: '',
|
||||
author: {
|
||||
firstName: 'Asabeneh',
|
||||
lastName: 'Yetayeh',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const Challenge = ({
|
||||
challenge: {
|
||||
name,
|
||||
description,
|
||||
status,
|
||||
days,
|
||||
level,
|
||||
duration,
|
||||
author: { firstName, lastName },
|
||||
},
|
||||
}) => (
|
||||
<div>
|
||||
<h1>{name}</h1>
|
||||
<p>{level}</p>
|
||||
<p>
|
||||
Author: {firstName} {lastName}
|
||||
</p>
|
||||
{duration && (
|
||||
<>
|
||||
{' '}
|
||||
<small>{duration}</small> <br />
|
||||
</>
|
||||
)}
|
||||
<small>Number of days: {days}</small>
|
||||
|
||||
<p>{description}</p>
|
||||
</div>
|
||||
)
|
||||
|
||||
const Challenges = (props) => {
|
||||
const path = props.location.pathname
|
||||
const slug = path.split('/').slice(path.split('/').length - 1)[0]
|
||||
const challenge = challenges.find((challenge) => challenge.slug === slug)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>30 Days Of React Challenge</h1>
|
||||
<ul>
|
||||
{challenges.map(({ name, slug }) => (
|
||||
<li key={slug}>
|
||||
<NavLink to={`/challenges/${slug}`}>{name}</NavLink>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<Switch>
|
||||
<Route
|
||||
exact
|
||||
path={'/challenges'}
|
||||
component={() => <h1>Choose any of the challenges</h1>}
|
||||
/>
|
||||
<Route
|
||||
path={path}
|
||||
component={(props) => <Challenge challenge={challenge} />}
|
||||
/>
|
||||
</Switch>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const NotFound = (props) => <h1>The page your looking for not found</h1>
|
||||
const Navbar = ({ username }) => (
|
||||
<ul>
|
||||
<li>
|
||||
<NavLink to='/'>Home</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
<NavLink to='/about'>About</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
<NavLink to='/contact'>Contact</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
<NavLink to={`/user/${username}`}>User</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
<NavLink to='/challenges'>Challenges</NavLink>
|
||||
</li>
|
||||
</ul>
|
||||
)
|
||||
|
||||
const User = ({ match, isLoggedIn, handleLogin }) => {
|
||||
const username = match.params.username
|
||||
return (
|
||||
<div>
|
||||
{isLoggedIn ? (
|
||||
<>
|
||||
<h1>Welcome {username} to the challenge</h1>
|
||||
<small>Now, you can navigate through all the challenges</small> <br />
|
||||
</>
|
||||
) : (
|
||||
<p>Please login in to access the challenges </p>
|
||||
)}
|
||||
<button onClick={handleLogin}>{isLoggedIn ? 'Logout' : 'Login'}</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Welcome = ({ handleLogin, isLoggedIn }) => {
|
||||
return (
|
||||
<div>
|
||||
{isLoggedIn ? 'Welcome to the challenge' : <p>Please login in </p>}
|
||||
<button onClick={handleLogin}>{isLoggedIn ? 'Logout' : 'Login'}</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
class App extends Component {
|
||||
state = {
|
||||
isLoggedIn: false,
|
||||
firstName: 'Asabeneh',
|
||||
}
|
||||
handleLogin = () => {
|
||||
this.setState({
|
||||
isLoggedIn: !this.state.isLoggedIn,
|
||||
})
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<Router>
|
||||
<div className='App'>
|
||||
<Navbar username={this.state.firstName} />
|
||||
|
||||
<Prompt
|
||||
message={({ pathname }) => {
|
||||
return this.state.isLoggedIn &&
|
||||
pathname.includes('/user/Asabeneh')
|
||||
? 'Are you sure you want to logout?'
|
||||
: true
|
||||
}}
|
||||
/>
|
||||
|
||||
<Switch>
|
||||
<Route path='/about' component={About} />
|
||||
<Route path='/contact' component={Contact} />
|
||||
<Route
|
||||
path='/user/:username'
|
||||
component={(props) => (
|
||||
<User
|
||||
{...props}
|
||||
isLoggedIn={this.state.isLoggedIn}
|
||||
handleLogin={this.handleLogin}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path='/login'
|
||||
component={(props) => (
|
||||
<Welcome
|
||||
{...props}
|
||||
isLoggedIn={this.state.isLoggedIn}
|
||||
handleLogin={this.handleLogin}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path='/challenges'
|
||||
component={(props) => {
|
||||
return this.state.isLoggedIn ? (
|
||||
<Challenges {...props} />
|
||||
) : (
|
||||
<Redirect to='/user/asabeneh' />
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<Route exact path='/' component={Home} />
|
||||
<Route component={NotFound} />
|
||||
</Switch>
|
||||
</div>
|
||||
</Router>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "30-days-of-react",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.3.2",
|
||||
"@testing-library/user-event": "^7.1.2",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-scripts": "3.4.3"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css?family=Montserrat:300,400,500|Roboto:300,400,500&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
|
||||
<title>30 Days Of React App</title>
|
||||
<style>
|
||||
|
||||
/* == General style === */
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
line-height: 1.5;
|
||||
font-family: 'Montserrat';
|
||||
font-weight: 300;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.root {
|
||||
min-height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.header-wrapper,
|
||||
.main-wrapper,
|
||||
.footer-wrapper {
|
||||
width: 85%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.header-wrapper,
|
||||
.main-wrapper {
|
||||
padding: 10px;
|
||||
margin: 2px auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 70px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
h2,
|
||||
h3 {
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: #61dbfb;
|
||||
padding: 25;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 10px;
|
||||
padding-bottom: 60px;
|
||||
/* Height of the footer */
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
ul li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
/* Height of the footer */
|
||||
background: #6cf;
|
||||
}
|
||||
|
||||
.footer-wrapper {
|
||||
font-weight: 400;
|
||||
text-align: center;
|
||||
line-height: 60px;
|
||||
}
|
||||
.user-card {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.user-card > img {
|
||||
border-radius: 50%;
|
||||
width: 14%;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,13 @@
|
||||
export const tenHighestPopulation = [
|
||||
{ country: 'World', population: 7693165599 },
|
||||
{ country: 'China', population: 1377422166 },
|
||||
{ country: 'India', population: 1295210000 },
|
||||
{ country: 'United States of America', population: 323947000 },
|
||||
{ country: 'Indonesia', population: 258705000 },
|
||||
{ country: 'Brazil', population: 206135893 },
|
||||
{ country: 'Pakistan', population: 194125062 },
|
||||
{ country: 'Nigeria', population: 186988000 },
|
||||
{ country: 'Bangladesh', population: 161006790 },
|
||||
{ country: 'Russian Federation', population: 146599183 },
|
||||
{ country: 'Japan', population: 126960000 },
|
||||
]
|
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 40 KiB |
@ -0,0 +1,84 @@
|
||||
import React, { Component } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import axios from 'axios'
|
||||
|
||||
const Country = ({
|
||||
country: { name, capital, flag, languages, population, currency },
|
||||
}) => {
|
||||
const formatedCapital =
|
||||
capital.length > 0 ? (
|
||||
<>
|
||||
<span>Capital: </span>
|
||||
{capital}
|
||||
</>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
const formatLanguage = languages.length > 1 ? `Languages` : `Language`
|
||||
console.log(languages)
|
||||
return (
|
||||
<div className='country'>
|
||||
<div className='country_flag'>
|
||||
<img src={flag} alt={name} />
|
||||
</div>
|
||||
<h3 className='country_name'>{name.toUpperCase()}</h3>
|
||||
<div class='country_text'>
|
||||
<p>{formatedCapital}</p>
|
||||
<p>
|
||||
<span>{formatLanguage}: </span>
|
||||
{languages.map((language) => language.name).join(', ')}
|
||||
</p>
|
||||
<p>
|
||||
<span>Population: </span>
|
||||
{population.toLocaleString()}
|
||||
</p>
|
||||
<p>
|
||||
<span>Currency: </span>
|
||||
{currency}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
class App extends Component {
|
||||
state = {
|
||||
data: [],
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchCountryData()
|
||||
}
|
||||
fetchCountryData = async () => {
|
||||
const url = 'https://restcountries.eu/rest/v2/all'
|
||||
try {
|
||||
const response = await axios.get(url)
|
||||
const data = await response.data
|
||||
this.setState({
|
||||
data,
|
||||
})
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='App'>
|
||||
<h1>React Component Life Cycle</h1>
|
||||
<h1>Calling API</h1>
|
||||
<div>
|
||||
<p>There are {this.state.data.length} countries in the api</p>
|
||||
<div className='countries-wrapper'>
|
||||
{this.state.data.map((country) => (
|
||||
<Country country={country} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|