@ -0,0 +1,451 @@
|
|||||||
|
<div align="center">
|
||||||
|
<h1> 30 Days Of React:Third Party Packages</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 14](../14_Day_Component_Life_Cycles/14_component_life_cycles.md) | [Day 16 >>]()
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- [Third Party Packages](#third-party-packages)
|
||||||
|
- [NPM or Yarn](#npm-or-yarn)
|
||||||
|
- [node-sass](#node-sass)
|
||||||
|
- [CSS modules](#css-modules)
|
||||||
|
- [axios](#axios)
|
||||||
|
- [react-icons](#react-icons)
|
||||||
|
- [moment](#moment)
|
||||||
|
- [styled-components](#styled-components)
|
||||||
|
- [reactstrap](#reactstrap)
|
||||||
|
- [lodash](#lodash)
|
||||||
|
- [Exercises](#exercises)
|
||||||
|
- [Exercises: Level 1](#exercises-level-1)
|
||||||
|
- [Exercises: Level 2](#exercises-level-2)
|
||||||
|
- [Exercises: Level 3](#exercises-level-3)
|
||||||
|
|
||||||
|
# Third Party Packages
|
||||||
|
|
||||||
|
There are more than 1.4M JavaScript packages on npm registry. By now there is a package almost for every kind of problem. We do not have to create the wheel instead we have to know how to use the wheel. In this section, we will learn how to use npm packages and also we will implement most common package for React applications. As of October 10, 2020, the npm registry popular packages, total number of packages, downloads per week and downloads per month seems as shown below.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
In one way or the other you many need the following packages in your React applications. Specially node-sass, moment and axios are important for some projects.
|
||||||
|
|
||||||
|
- [node-sass](https://www.npmjs.com/package/node-sass)
|
||||||
|
- [moment](https://www.npmjs.com/package/moment)
|
||||||
|
- [axios](https://www.npmjs.com/package/axios)
|
||||||
|
- [react-icons](https://react-icons.github.io/react-icons/)
|
||||||
|
- [styled-components](https://styled-components.com/)
|
||||||
|
- [reactstrap](https://reactstrap.github.io/)
|
||||||
|
- [lodash](https://www.npmjs.com/package/lodash)
|
||||||
|
- [uuid](https://www.npmjs.com/package/uuid)
|
||||||
|
|
||||||
|
## NPM or Yarn
|
||||||
|
|
||||||
|
You can use either npm or yarn to install packages. If you want to use [yarn](https://yarnpkg.com) you have install it separately. I would recommend you to stick in one of the package. Don't use both package management tools in one application at the same time.
|
||||||
|
|
||||||
|
Let's see how to install packages to an application. First, we go to the project directory and write the following command.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
// syntax, we can use i or install
|
||||||
|
npm i package-name
|
||||||
|
// or
|
||||||
|
yarn add package-name
|
||||||
|
```
|
||||||
|
|
||||||
|
### node-sass
|
||||||
|
|
||||||
|
Sass is a CSS preprocess which allows to write CSS function, nesting and many more. Let's install node-sass to make use of the power of Sass.
|
||||||
|
|
||||||
|
Using npm:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
Asabeneh@DESKTOP-KGC1AKC MINGW64 ~/Desktop/30-days-of-react$ npm install node-sass
|
||||||
|
```
|
||||||
|
|
||||||
|
Using yarn:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
Asabeneh@DESKTOP-KGC1AKC MINGW64 ~/Desktop/30-days-of-react$ yarn add node-sass
|
||||||
|
```
|
||||||
|
|
||||||
|
After installing node-sass you can start using Sass in React. Create a styles folder and inside this folder create test.scss. Import this file to the component you are working or index.js. You don't need import the node-sass to the component.
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* ./styles/header.scss */
|
||||||
|
header {
|
||||||
|
background-color: #61dbfb;
|
||||||
|
padding: 25;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Header.js
|
||||||
|
import React from 'react'
|
||||||
|
import './styles/header.scss
|
||||||
|
const Header = () = (
|
||||||
|
<header>
|
||||||
|
<div className='header-wrapper'>
|
||||||
|
<h1>30 Days Of React</h1>
|
||||||
|
<h2>Getting Started React</h2>
|
||||||
|
<h3>JavaScript Library</h3>
|
||||||
|
<p>Instructor: Asabeneh Yetayeh</p>
|
||||||
|
<small>Oct 15, 2020</small>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Header
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
// App.js
|
||||||
|
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import ReactDOM from 'react-dom'
|
||||||
|
import './styles/header.scss
|
||||||
|
|
||||||
|
class App extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className='App'>
|
||||||
|
<Header />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root')
|
||||||
|
ReactDOM.render(<App />, rootElement)
|
||||||
|
```
|
||||||
|
|
||||||
|
### CSS modules
|
||||||
|
|
||||||
|
In addition to Sass, it is good to know how to use CSS modules in React. We do not have to install a separate package for a CSS module to use CSS module in React applications. CSS module can be used with Pure CSS or with Sass. The naming convention for CSS module is a specific name followed by dot and module(test.module.css or test.module.scss)
|
||||||
|
|
||||||
|
Naming:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// naming for Sass
|
||||||
|
// naming for CSS
|
||||||
|
;[name].module.scss[name].module.css
|
||||||
|
```
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* ./styles/header.module.scss */
|
||||||
|
.header {
|
||||||
|
background-color: #61dbfb;
|
||||||
|
padding: 25;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.header-wrapper {
|
||||||
|
font-weight:500
|
||||||
|
border: 5px solid orange;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Header.js
|
||||||
|
import React from 'react'
|
||||||
|
import headerStyles from './styles/header.module.scss
|
||||||
|
// We can all destructure the class name
|
||||||
|
const {header, headerWrapper} = headerStyles
|
||||||
|
const Header = () = (
|
||||||
|
<header className = {headerStyles.header}>
|
||||||
|
<div className={headerStyles.headerWrapper}>
|
||||||
|
<h1>30 Days Of React</h1>
|
||||||
|
<h2>Getting Started React</h2>
|
||||||
|
<h3>JavaScript Library</h3>
|
||||||
|
<p>Instructor: Asabeneh Yetayeh</p>
|
||||||
|
<small>Oct 15, 2020</small>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Header
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
// App.js
|
||||||
|
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import ReactDOM from 'react-dom'
|
||||||
|
import './styles/header.scss
|
||||||
|
|
||||||
|
class App extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className='App'>
|
||||||
|
<Header />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root')
|
||||||
|
ReactDOM.render(<App />, rootElement)
|
||||||
|
```
|
||||||
|
|
||||||
|
### axios
|
||||||
|
|
||||||
|
Axios is a JavaScript library which can make HTTP requests to fetch data. In this section we will see on a get request. However, it is possible to do all the request types using [axios](https://github.com/axios/axios) (GET, POST, PUT, PATCH, DELETE).
|
||||||
|
|
||||||
|
Using npm:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
Asabeneh@DESKTOP-KGC1AKC MINGW64 ~/Desktop/30-days-of-react$ npm install axios
|
||||||
|
```
|
||||||
|
|
||||||
|
Using yarn:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
Asabeneh@DESKTOP-KGC1AKC MINGW64 ~/Desktop/30-days-of-react$ yarn add axios
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
// axios is a package which
|
||||||
|
// send requests to a server to fetch data
|
||||||
|
import axios from 'axios'
|
||||||
|
import ReactDOM from 'react-dom'
|
||||||
|
|
||||||
|
class App extends Component {
|
||||||
|
state = {
|
||||||
|
data: [],
|
||||||
|
}
|
||||||
|
componentDidMount() {
|
||||||
|
const API_URL = 'https://restcountries.eu/rest/v2/all'
|
||||||
|
axios
|
||||||
|
.get(API_URL)
|
||||||
|
.then((response) => {
|
||||||
|
this.setState({
|
||||||
|
data: response.data,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
renderCountries = () => {
|
||||||
|
return this.state.data.map((country) => {
|
||||||
|
const languageOrLanguages =
|
||||||
|
country.languages.length > 1 ? 'Langauges' : 'Language'
|
||||||
|
const formatLanguages = country.languages
|
||||||
|
.map(({ name }) => name)
|
||||||
|
.join(', ')
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
{' '}
|
||||||
|
<img src={country.flag} alt={country.name} />{' '}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h1>{country.name}</h1>
|
||||||
|
<p>Capital: {country.capital}</p>
|
||||||
|
<p>
|
||||||
|
{languageOrLanguages}: {formatLanguages}
|
||||||
|
</p>
|
||||||
|
<p>Population: {country.population}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className='App'>
|
||||||
|
<h1>Fetching Data Using Axios</h1>
|
||||||
|
<div>
|
||||||
|
<p>There are {this.state.data.length} countries in the api</p>
|
||||||
|
<div className='countries-wrapper'>{this.renderCountries()}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root')
|
||||||
|
ReactDOM.render(<App />, rootElement)
|
||||||
|
```
|
||||||
|
|
||||||
|
We can use axios with await and async functions. In order to implement await and async we need to have separate function outside the componentDidMount. If we implement await and async the error has to be handled by try and catch.
|
||||||
|
|
||||||
|
### react-icons
|
||||||
|
|
||||||
|
Icons are integral part of a website. To get different SVG icons
|
||||||
|
|
||||||
|
Using npm:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
Asabeneh@DESKTOP-KGC1AKC MINGW64 ~/Desktop/30-days-of-react$ npm install react-icons
|
||||||
|
```
|
||||||
|
|
||||||
|
Using yarn:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
Asabeneh@DESKTOP-KGC1AKC MINGW64 ~/Desktop/30-days-of-react$ yarn add react-icons
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import axios from 'axios'
|
||||||
|
import ReactDOM from 'react-dom'
|
||||||
|
import moment from 'moment'
|
||||||
|
import {
|
||||||
|
TiSocialLinkedinCircular,
|
||||||
|
TiSocialGithubCircular,
|
||||||
|
TiSocialTwitterCircular,
|
||||||
|
} from 'react-icons/ti'
|
||||||
|
|
||||||
|
const Footer = () => (
|
||||||
|
<footer>
|
||||||
|
<h3>30 Days Of React</h3>
|
||||||
|
<div>
|
||||||
|
<TiSocialLinkedinCircular />
|
||||||
|
<TiSocialGithubCircular />
|
||||||
|
<TiSocialTwitterCircular />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<small> Copyright © {new Date().getFullYear()} </small>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
)
|
||||||
|
|
||||||
|
class App extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className='App'>
|
||||||
|
<h1>Welcome to the world of Icons</h1>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root')
|
||||||
|
ReactDOM.render(<App />, rootElement)
|
||||||
|
```
|
||||||
|
|
||||||
|
### moment
|
||||||
|
|
||||||
|
Moment is a small JavaScript library which gives us different time formats.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install moment
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import ReactDOM from 'react-dom'
|
||||||
|
|
||||||
|
class App extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className='App'>
|
||||||
|
<h1>How to use moment</h1>
|
||||||
|
<p>This challenge was started {moment('2020-10-01').fromNow()}</p>
|
||||||
|
<p>The challenge will be over in {moment('2020-10-30').fromNow()}</p>
|
||||||
|
<p>Today is {moment(new Date()).format('MMMM DD, YYYY HH:mm')}</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root')
|
||||||
|
ReactDOM.render(<App />, rootElement)
|
||||||
|
```
|
||||||
|
|
||||||
|
### styled-components
|
||||||
|
|
||||||
|
It uses a tagged template literals to style a component. It removes the mapping between components and styles. This means that when you're defining your styles, you're actually creating a normal React component, that has your styles attached to it.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import ReactDOM from 'react-dom'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
const Title = styled.h1`
|
||||||
|
font-size: 70px;
|
||||||
|
font-weight: 300;
|
||||||
|
`
|
||||||
|
const Header = styled.header`
|
||||||
|
background-color: #61dbfb;
|
||||||
|
padding: 25;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 0;
|
||||||
|
`
|
||||||
|
|
||||||
|
class App extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className='App'>
|
||||||
|
<Header>
|
||||||
|
<div>
|
||||||
|
<Title>30 Days Of React</Title>
|
||||||
|
<h2>Getting Started React</h2>
|
||||||
|
<h3>JavaScript Library</h3>
|
||||||
|
<p>Instructor: Asabeneh Yetayeh</p>
|
||||||
|
<small>Oct 15, 2020</small>
|
||||||
|
</div>
|
||||||
|
</Header>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root')
|
||||||
|
ReactDOM.render(<App />, rootElement)
|
||||||
|
```
|
||||||
|
|
||||||
|
### reactstrap
|
||||||
|
|
||||||
|
The [reactstrap](https://reactstrap.github.io/) package allows to use a component with bootstrap.
|
||||||
|
|
||||||
|
### lodash
|
||||||
|
|
||||||
|
According to the official lodash documentation, 'A modern JavaScript utility library delivering modularity, performance & extras.'
|
||||||
|
|
||||||
|
Try to also learn how to use the package _classnames_ and _validator_.
|
||||||
|
|
||||||
|
# Exercises
|
||||||
|
|
||||||
|
## Exercises: Level 1
|
||||||
|
|
||||||
|
1. What is a package?
|
||||||
|
2. What is a third party package ?
|
||||||
|
3. Do you have to use third party packages?
|
||||||
|
4. How do you know the popularity and stability of a third party package?
|
||||||
|
5. How many JavaScript packages are there on the npm registry?
|
||||||
|
6. How do you install a third party package?
|
||||||
|
7. What packages do you use most frequently?
|
||||||
|
8. What package do you use to fetch data?
|
||||||
|
9. What is the purpose of classnames package?
|
||||||
|
10. What is the pupose validator package?
|
||||||
|
|
||||||
|
## Exercises: Level 2
|
||||||
|
|
||||||
|
1. Learn how to use Sass
|
||||||
|
2. Learn how to use axios
|
||||||
|
3. Learn how to use moment and react-icons
|
||||||
|
4. Use the validator package to validate the form you had in day 12
|
||||||
|
5. Use classnames to change a class based on some logic.
|
||||||
|
|
||||||
|
## Exercises: Level 3
|
||||||
|
|
||||||
|
🎉 CONGRATULATIONS ! 🎉
|
||||||
|
|
||||||
|
[<< Day 14](../14_Day_Component_Life_Cycles/14_component_life_cycles.md) | [Day 16 >>]()
|
@ -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,5 @@
|
|||||||
|
# 30 Days of React App: Day 15
|
||||||
|
|
||||||
|
In the project directory, you can run to start the project
|
||||||
|
|
||||||
|
### `npm start`
|
@ -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,137 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import axios from 'axios'
|
||||||
|
import ReactDOM from 'react-dom'
|
||||||
|
import moment from 'moment'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import {
|
||||||
|
TiSocialLinkedinCircular,
|
||||||
|
TiSocialGithubCircular,
|
||||||
|
TiSocialTwitterCircular,
|
||||||
|
} from 'react-icons/ti'
|
||||||
|
|
||||||
|
const Title = styled.h1`
|
||||||
|
font-size: 70px;
|
||||||
|
font-weight: 300;
|
||||||
|
`
|
||||||
|
const SubTitle = styled.h2`
|
||||||
|
font-weight: 300;
|
||||||
|
`
|
||||||
|
|
||||||
|
const Header = styled.header`
|
||||||
|
background-color: #61dbfb;
|
||||||
|
padding: 25;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 0;
|
||||||
|
`
|
||||||
|
|
||||||
|
class App extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
console.log('I am the constructor and I will be the first to run.')
|
||||||
|
this.state = {
|
||||||
|
firstName: 'John',
|
||||||
|
data: [],
|
||||||
|
day: 1,
|
||||||
|
congratulate: '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const API_URL = 'https://restcountries.eu/rest/v2/all'
|
||||||
|
axios
|
||||||
|
.get(API_URL)
|
||||||
|
.then((response) => {
|
||||||
|
this.setState({
|
||||||
|
data: response.data,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
static getDerivedStateFromProps(props, state) {
|
||||||
|
return { firstName: props.firstName }
|
||||||
|
}
|
||||||
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
console.log(nextProps, nextState)
|
||||||
|
console.log(nextState.day)
|
||||||
|
if (nextState.day > 31) {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doChallenge = () => {
|
||||||
|
this.setState({
|
||||||
|
day: this.state.day + 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
renderCountries = () => {
|
||||||
|
return this.state.data.map((country) => {
|
||||||
|
const languageOrLanguages =
|
||||||
|
country.languages.length > 1 ? 'Langauges' : 'Language'
|
||||||
|
const formatLanguages = country.languages
|
||||||
|
.map(({ name }) => name)
|
||||||
|
.join(', ')
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
{' '}
|
||||||
|
<img src={country.flag} alt={country.name} />{' '}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h1>{country.name}</h1>
|
||||||
|
<p>Capital: {country.capital}</p>
|
||||||
|
<p>
|
||||||
|
{languageOrLanguages}: {formatLanguages}
|
||||||
|
</p>
|
||||||
|
<p>Population: {country.population}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
if (prevState.day == 30) {
|
||||||
|
this.setState({
|
||||||
|
congratulate: 'Congratulations,Challenge has been completed',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
console.log(prevState, prevProps)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className='App'>
|
||||||
|
<Header>
|
||||||
|
<Title>30 Days Of React</Title>
|
||||||
|
<h2>Getting Started React</h2>
|
||||||
|
<h3>JavaScript Library</h3>
|
||||||
|
<p>Instructor: Asabeneh Yetayey</p>
|
||||||
|
<small>Oct 15, 2020</small>
|
||||||
|
</Header>
|
||||||
|
<p>This challenge was started {moment('2020-10-01').fromNow()}</p>
|
||||||
|
<p>The challenge will be over in {moment('2020-10-30').fromNow()}</p>
|
||||||
|
<p>Today is {moment(new Date()).format('MMMM DD, YYYY HH:mm')}</p>
|
||||||
|
<h1>React Component Life Cycle</h1>
|
||||||
|
<h1>Calling API</h1>
|
||||||
|
<TiSocialLinkedinCircular />
|
||||||
|
<TiSocialGithubCircular />
|
||||||
|
<TiSocialTwitterCircular />
|
||||||
|
|
||||||
|
<button onClick={this.doChallenge}>Do Challenge</button>
|
||||||
|
<p>Challenge: Day {this.state.day}</p>
|
||||||
|
{this.state.congratulate && <h2>{this.state.congratulate}</h2>}
|
||||||
|
<div>
|
||||||
|
<p>There are {this.state.data.length} countries in the api</p>
|
||||||
|
<div className='countries-wrapper'>{this.renderCountries()}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root')
|
||||||
|
ReactDOM.render(<App />, rootElement)
|
@ -0,0 +1,212 @@
|
|||||||
|
<div align="center">
|
||||||
|
<h1> 30 Days Of React: Higher Order Component</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 15](../15_Third_Party_Packages/15_third_party_packages.md) | [Day 17 >>](../17_React_Router/17_react_router.md)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- [Higher Order Component](#higher-order-component)
|
||||||
|
- [Exercises](#exercises)
|
||||||
|
- [Exercises: Level 1](#exercises-level-1)
|
||||||
|
- [Exercises: Level 2](#exercises-level-2)
|
||||||
|
- [Exercises: Level 3](#exercises-level-3)
|
||||||
|
|
||||||
|
# Higher Order Component
|
||||||
|
|
||||||
|
The term higher order component is similar to higher order function in JavaScript. In JavaScript, a higher order function is a function that takes another function as a parameter or return another function.
|
||||||
|
|
||||||
|
Similar to higher order function, a higher order component takes a component and return another component.
|
||||||
|
This definition will make sense with examples. Look at the example below for better understand.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// One way of writing a Higher Order Component(HOC)
|
||||||
|
import React from 'react'
|
||||||
|
const higherOrderComponent = (Component) => {
|
||||||
|
return (props) => {
|
||||||
|
return <Component {...props} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Most of the time third party libraries use higher order component. For instance redux, react-router-dom and material-u use higher order component.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const Button = ({ onClick, text, style }) => {
|
||||||
|
return (
|
||||||
|
<button onClick={onClick} style={style}>
|
||||||
|
{text}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const buttonWithStyle = (CompParam) => {
|
||||||
|
const buttonStyles = {
|
||||||
|
backgroundColor: '#61dbfb',
|
||||||
|
padding: '10px 25px',
|
||||||
|
border: 'none',
|
||||||
|
borderRadius: 5,
|
||||||
|
margin: 3,
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: 18,
|
||||||
|
color: 'white',
|
||||||
|
}
|
||||||
|
return (props) => {
|
||||||
|
return <CompParam {...props} style={buttonStyles} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const NewButton = buttonWithSuperPower(Button)
|
||||||
|
|
||||||
|
class App extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className='App'>
|
||||||
|
<Button text='No Style' />
|
||||||
|
<NewButton text='Styled Button' />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root')
|
||||||
|
ReactDOM.render(<App />, rootElement)
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's make the buttonWithStyle higher order take more parameter in addition to the component.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const Button = ({ onClick, text, style }) => {
|
||||||
|
return (
|
||||||
|
<button onClick={onClick} style={style}>
|
||||||
|
{text}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const buttonWithStyles = (CompParam, name = 'default') => {
|
||||||
|
const colors = [
|
||||||
|
{
|
||||||
|
name: 'default',
|
||||||
|
backgroundColor: '#e7e7e7',
|
||||||
|
color: '#000000',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'react',
|
||||||
|
backgroundColor: '#61dbfb',
|
||||||
|
color: '#ffffff',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'success',
|
||||||
|
backgroundColor: '#4CAF50',
|
||||||
|
color: '#ffffff',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'info',
|
||||||
|
backgroundColor: '#2196F3',
|
||||||
|
color: '#ffffff',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'warning',
|
||||||
|
backgroundColor: '#ff9800',
|
||||||
|
color: '#ffffff',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'danger',
|
||||||
|
backgroundColor: '#f44336',
|
||||||
|
color: '#ffffff',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
const { backgroundColor, color } = colors.find((c) => c.name === name)
|
||||||
|
|
||||||
|
const buttonStyles = {
|
||||||
|
backgroundColor,
|
||||||
|
padding: '10px 45px',
|
||||||
|
border: 'none',
|
||||||
|
borderRadius: 3,
|
||||||
|
margin: 3,
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: '1.25rem',
|
||||||
|
color,
|
||||||
|
}
|
||||||
|
return (props) => {
|
||||||
|
return <CompParam {...props} style={buttonStyles} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const NewButton = buttonWithSuperPower(Button)
|
||||||
|
const ReactButton = buttonWithSuperPower(Button, 'react')
|
||||||
|
const InfoButton = buttonWithSuperPower(Button, 'info')
|
||||||
|
const SuccessButton = buttonWithSuperPower(Button, 'success')
|
||||||
|
const WarningButton = buttonWithSuperPower(Button, 'warning')
|
||||||
|
const DangerButton = buttonWithSuperPower(Button, 'danger')
|
||||||
|
|
||||||
|
class App extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className='App'>
|
||||||
|
<Button text='No Style' onClick={() => alert('I am not styled yet')} />
|
||||||
|
<NewButton
|
||||||
|
text='Styled Button'
|
||||||
|
onClick={() => alert('I am the default style')}
|
||||||
|
/>
|
||||||
|
<ReactButton text='React' onClick={() => alert('I have react color')} />
|
||||||
|
<InfoButton
|
||||||
|
text='Info'
|
||||||
|
onClick={() => alert('I am styled with info color')}
|
||||||
|
/>
|
||||||
|
<SuccessButton text='Success' onClick={() => alert('I am successful')} />
|
||||||
|
<WarningButton
|
||||||
|
text='Warning'
|
||||||
|
onClick={() => alert('I warn you many times')}
|
||||||
|
/>
|
||||||
|
<DangerButton
|
||||||
|
text='Danger'
|
||||||
|
onClick={() => alert('Oh no, you can not restore it')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root')
|
||||||
|
ReactDOM.render(<App />, rootElement)
|
||||||
|
```
|
||||||
|
|
||||||
|
The is above example is one use case of Higher Order Component. However, its use case is is more than just styling simple button. It has enormous use cases, it allow us to reuse component and enhance a component with style and functionality. In the coming sections, we will cover React Router and we will use HOC and you will not be surprised when you see one component wrap another component.
|
||||||
|
|
||||||
|
# Exercises
|
||||||
|
|
||||||
|
## Exercises: Level 1
|
||||||
|
|
||||||
|
1. What is higher order function
|
||||||
|
2. What is Higher Order Component
|
||||||
|
3. What is the difference between higher order function and higher order component?
|
||||||
|
4. A higher order component can allow us to enhance a component. (T or F)
|
||||||
|
|
||||||
|
## Exercises: Level 2
|
||||||
|
|
||||||
|
1. Make a higher order component which can handle all the input type.
|
||||||
|
|
||||||
|
## Exercises: Level 3
|
||||||
|
|
||||||
|
coming
|
||||||
|
|
||||||
|
🎉 CONGRATULATIONS ! 🎉
|
||||||
|
|
||||||
|
[<< Day 15](../15_Third_Party_Packages/15_third_party_packages.md) | [Day 17 >>](../17_React_Router/17_react_router.md)
|
@ -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,5 @@
|
|||||||
|
# 30 Days of React App: Day 16
|
||||||
|
|
||||||
|
In the project directory, you can run to start the project
|
||||||
|
|
||||||
|
### `npm start`
|
@ -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,99 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import ReactDOM from 'react-dom'
|
||||||
|
|
||||||
|
const Button = ({ onClick, text, style }) => {
|
||||||
|
return (
|
||||||
|
<button onClick={onClick} style={style}>
|
||||||
|
{text}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const buttonWithStyles = (CompParam, name = 'default') => {
|
||||||
|
const colors = [
|
||||||
|
{
|
||||||
|
name: 'default',
|
||||||
|
backgroundColor: '#e7e7e7',
|
||||||
|
color: '#000000',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'react',
|
||||||
|
backgroundColor: '#61dbfb',
|
||||||
|
color: '#ffffff',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'success',
|
||||||
|
backgroundColor: '#4CAF50',
|
||||||
|
color: '#ffffff',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'info',
|
||||||
|
backgroundColor: '#2196F3',
|
||||||
|
color: '#ffffff',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'warning',
|
||||||
|
backgroundColor: '#ff9800',
|
||||||
|
color: '#ffffff',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'danger',
|
||||||
|
backgroundColor: '#f44336',
|
||||||
|
color: '#ffffff',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
const { backgroundColor, color } = colors.find((c) => c.name === name)
|
||||||
|
|
||||||
|
const buttonStyles = {
|
||||||
|
backgroundColor,
|
||||||
|
padding: '10px 45px',
|
||||||
|
border: 'none',
|
||||||
|
borderRadius: 3,
|
||||||
|
margin: 3,
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: '1.25rem',
|
||||||
|
color,
|
||||||
|
}
|
||||||
|
return (props) => {
|
||||||
|
return <CompParam {...props} style={buttonStyles} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const NewButton = buttonWithStyles(Button)
|
||||||
|
const ReactButton = buttonWithStyles(Button, 'react')
|
||||||
|
const InfoButton = buttonWithStyles(Button, 'info')
|
||||||
|
const WarningButton = buttonWithStyles(Button, 'warning')
|
||||||
|
const DangerButton = buttonWithStyles(Button, 'danger')
|
||||||
|
const SuccessButton = buttonWithStyles(Button, 'success')
|
||||||
|
|
||||||
|
class App extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className='App'>
|
||||||
|
<h1>Higher Order Components</h1>
|
||||||
|
<Button text='No Style' onClick={() => alert('I am not styled yet')} />
|
||||||
|
<NewButton
|
||||||
|
text='Styled Button'
|
||||||
|
onClick={() => alert('I am the default style')}
|
||||||
|
/>
|
||||||
|
<ReactButton text='React' onClick={() => alert('I have react color')} />
|
||||||
|
<InfoButton
|
||||||
|
text='Info'
|
||||||
|
onClick={() => alert('I am styled with info color')}
|
||||||
|
/>
|
||||||
|
<SuccessButton text='Success' onClick={() => alert('I am successful')} />
|
||||||
|
<WarningButton
|
||||||
|
text='Warning'
|
||||||
|
onClick={() => alert('I warn you many times')}
|
||||||
|
/>
|
||||||
|
<DangerButton
|
||||||
|
text='Danger'
|
||||||
|
onClick={() => alert('Oh no, you can not restore it')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,5 @@
|
|||||||
|
# 30 Days of React App: Day 17
|
||||||
|
|
||||||
|
In the project directory, you can run to start the project
|
||||||
|
|
||||||
|
### `npm start`
|
@ -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,303 @@
|
|||||||
|
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,5 @@
|
|||||||
|
# 30 Days of React App: Day 19
|
||||||
|
|
||||||
|
In the project directory, you can run to start the project
|
||||||
|
|
||||||
|
### `npm start`
|
@ -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)
|
@ -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>>]()
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- [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>>]()
|
@ -0,0 +1,35 @@
|
|||||||
|
<div align="center">
|
||||||
|
<h1> 30 Days Of React: Projects</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 18](../18_Fetch_And_Axios/18_fetch_axios.md) | [Day 20>>]()
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- [Projects](#projects)
|
||||||
|
- [Exercises](#exercises)
|
||||||
|
- [Exercises: Level 1](#exercises-level-1)
|
||||||
|
|
||||||
|
# Projects
|
||||||
|
|
||||||
|
Congratulations for making it to this far. Now, you have a solid understanding of React. I believe we have covered most important features of React and your are ready to work on projects. What we have covered so far is the old version of React. Starting from Day 20, we will learn React Hooks. In the next three days you will work on projects only.
|
||||||
|
|
||||||
|
# Exercises
|
||||||
|
|
||||||
|
## Exercises: Level 1
|
||||||
|
|
||||||
|
🎉 CONGRATULATIONS ! 🎉
|
||||||
|
|
||||||
|
[<< Day 18](../18_Fetch_And_Axios/18_fetch_axios.md) | [Day 20>>]()
|
@ -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,5 @@
|
|||||||
|
# 30 Days of React App: Day 17
|
||||||
|
|
||||||
|
In the project directory, you can run to start the project
|
||||||
|
|
||||||
|
### `npm start`
|
@ -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)
|
After Width: | Height: | Size: 113 KiB |
After Width: | Height: | Size: 113 KiB |
After Width: | Height: | Size: 113 KiB |
After Width: | Height: | Size: 113 KiB |
After Width: | Height: | Size: 113 KiB |
After Width: | Height: | Size: 94 KiB |