@ -0,0 +1,89 @@
|
||||
/* == 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%;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=index.css.map */
|
@ -0,0 +1 @@
|
||||
{"version":3,"sourceRoot":"","sources":["index.scss"],"names":[],"mappings":"AAAA;AAEA;EACE;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;AAAA;AAAA;EAGE;EACA;;;AAGF;AAAA;EAEE;EACA;;;AAGF;EACE;EACA;;;AAGF;AAAA;EAEE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;AACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;AACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA","file":"index.css"}
|
@ -0,0 +1,99 @@
|
||||
<div align="center">
|
||||
<h1> 30 Days Of React: Fetching Data Using Hooks</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 22](../22_Form_Using_Hooks/22_form_using_hooks.md) | [Day 24>>](../24_projects/24_projects.md)
|
||||
|
||||

|
||||
|
||||
- [Introducing React Hook](#introducing-react-hook)
|
||||
- [Basic Hooks](#basic-hooks)
|
||||
- [State Hook](#state-hook)
|
||||
- [Effect Hook](#effect-hook)
|
||||
- [Context Hook](#context-hook)
|
||||
- [Additional Hook](#additional-hook)
|
||||
- [Exercises](#exercises)
|
||||
- [Exercises: Level 1](#exercises-level-1)
|
||||
|
||||
# Fetching Data Using Hooks
|
||||
|
||||
In the previous sections, you have learned how to fetch data using fetch and axios. In this section , we will use the useEffect hook to fetch data. We can use fetch or axios but I prefer to use axios. In React hooks, you don't have to use componentDidMount life cycle separately to fetch data. The useEffect has incorporate the React life cycle methods(mounting, updating and unmounting). Let's convert the code we wrote on day 18 to React hooks. We need to import the useEffect from react. The useEffect takes to argument that is a callback and an array. If the array is empty it behaves as componentDidMount life cycle where if the array has other properties, it will behave as updating too.
|
||||
|
||||
```js
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import axios from 'axios'
|
||||
import ReactDOM, { findDOMNode } from 'react-dom'
|
||||
|
||||
const Country = ({ country: { name, flag, population } }) => {
|
||||
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>
|
||||
<span>Population: </span>
|
||||
{population}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const App = (props) => {
|
||||
// setting initial state and method to update state
|
||||
const [data, setData] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
fetchData()
|
||||
}, [])
|
||||
|
||||
const fetchData = async () => {
|
||||
const url = 'https://restcountries.eu/rest/v2/all'
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
const data = await response.json()
|
||||
setData(data)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='App'>
|
||||
<h1>Fetching Data Using Hook</h1>
|
||||
<h1>Calling API</h1>
|
||||
<div>
|
||||
<p>There are {data.length} countries in the api</p>
|
||||
<div className='countries-wrapper'>
|
||||
{data.map((country) => (
|
||||
<Country country={country} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
# Exercises
|
||||
|
||||
🎉 CONGRATULATIONS ! 🎉
|
||||
|
||||
[<< Day 22](../22_Form_Using_Hooks/22_form_using_hooks.md) | [Day 24>>](../24_projects/24_projects.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 23
|
||||
|
||||
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,57 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import axios from 'axios'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
const Country = ({ country: { name, flag } }) => {
|
||||
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>
|
||||
<span>Population: </span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const App = (props) => {
|
||||
// setting initial state and method to update state
|
||||
const [data, setData] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
fetchData()
|
||||
}, [])
|
||||
|
||||
const fetchData = async () => {
|
||||
const url = 'https://restcountries.eu/rest/v2/all'
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
const data = await response.json()
|
||||
setData(data)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='App'>
|
||||
<h1>Fetching Data Using Hook</h1>
|
||||
<h1>Calling API</h1>
|
||||
<div>
|
||||
<p>There are {data.length} countries in the api</p>
|
||||
<div className='countries-wrapper'>
|
||||
{data.map((country) => (
|
||||
<Country country={country} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
@ -0,0 +1,86 @@
|
||||
/* == 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%;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
<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 23](../23_Fetching_Data_Using_Hooks/23_fetching_data_using_hooks.md) | [Day 25>>](../25_Custom_Hooks/25_custom_hooks.md)
|
||||
|
||||

|
||||
|
||||
# Project Using React Hooks
|
||||
|
||||
# Exercises
|
||||
|
||||
1 Build the following application using [countries API](https://restcountries.eu/rest/v2/all).
|
||||
[DEMO](https://www.30daysofreact.com/day-23/countries-data)
|
||||
|
||||
🎉 CONGRATULATIONS ! 🎉
|
||||
|
||||
[<< Day 23](../23_Fetching_Data_Using_Hooks/23_fetching_data_using_hooks.md) | [Day 25>>](../25_Custom_Hooks/25_custom_hooks.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 24
|
||||
|
||||
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,57 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import axios from 'axios'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
const Country = ({ country: { name, flag } }) => {
|
||||
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>
|
||||
<span>Population: </span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const App = (props) => {
|
||||
// setting initial state and method to update state
|
||||
const [data, setData] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
fetchData()
|
||||
}, [])
|
||||
|
||||
const fetchData = async () => {
|
||||
const url = 'https://restcountries.eu/rest/v2/all'
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
const data = await response.json()
|
||||
setData(data)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='App'>
|
||||
<h1>Fetching Data Using Hook</h1>
|
||||
<h1>Calling API</h1>
|
||||
<div>
|
||||
<p>There are {data.length} countries in the api</p>
|
||||
<div className='countries-wrapper'>
|
||||
{data.map((country) => (
|
||||
<Country country={country} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
@ -0,0 +1,86 @@
|
||||
/* == 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%;
|
||||
}
|
@ -0,0 +1,228 @@
|
||||
<div align="center">
|
||||
<h1> 30 Days Of React: Custom Hooks</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 24](../24_projects/24_projects.md) | [Day 26>>](../26_Context/26_context.md)
|
||||
|
||||

|
||||
|
||||
# Custom Hooks
|
||||
|
||||
It is possible to make a custom hook on top of the available React hooks. For instance, when we fetch data we with use either fetch or axios to send an HTTP request and useEffect hooks to manage the React life cycle. Let's build useFetch custom hook on top of useEffect and useState.
|
||||
|
||||
We wrote this snippet of code in the previous section and we use useEffect hooks to fetch data from API. Now, let's convert this code to a custom hook. The naming convention for a custom hook is camelCase and it starts with the word use that is why we called our custom hook, useFetch.
|
||||
|
||||
```js
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import axios from 'axios'
|
||||
import ReactDOM, { findDOMNode } from 'react-dom'
|
||||
|
||||
const Country = ({ country: { name, flag, population } }) => {
|
||||
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>
|
||||
<span>Population: </span>
|
||||
{population}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const App = (props) => {
|
||||
// setting initial state and method to update state
|
||||
const [data, setData] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
fetchData()
|
||||
}, [])
|
||||
|
||||
const fetchData = async () => {
|
||||
const url = 'https://restcountries.eu/rest/v2/all'
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
const data = await response.json()
|
||||
setData(data)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='App'>
|
||||
<h1>Fetching Data Using Hook</h1>
|
||||
<h1>Calling API</h1>
|
||||
<div>
|
||||
<p>There are {data.length} countries in the api</p>
|
||||
<div className='countries-wrapper'>
|
||||
{data.map((country) => (
|
||||
<Country country={country} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
Create a file name useFetch.js, and import useState and useEffect. Then transfer the state, useEffect and fetchData function part of the above code to the useFetch.js.
|
||||
|
||||
```js
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
const useFetch = () => {
|
||||
const [data, setData] = useState([])
|
||||
|
||||
const fetchData = async () => {
|
||||
const url = 'https://restcountries.eu/rest/v2/all'
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
const data = await response.json()
|
||||
setData(data)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchData()
|
||||
}, [])
|
||||
}
|
||||
```
|
||||
|
||||
Then let's make the useFetch function to take a parameter. When we fetch data the only thing which changes is the API therefore let's pass a URL parameter for the function.
|
||||
|
||||
```js
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
const useFetch = (url) => {
|
||||
const [data, setData] = useState([])
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
const data = await response.json()
|
||||
setData(data)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchData()
|
||||
}, [])
|
||||
}
|
||||
|
||||
export default useFetch
|
||||
```
|
||||
|
||||
With the above code, we should manage to fetch the data but it is advisable to put the function in the useEffect and let's move the function code the useEffect.
|
||||
|
||||
```js
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
export const useFetch = (url) => {
|
||||
const [data, setData] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
const data = await response.json()
|
||||
setData(data)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
fetchData()
|
||||
}, [url])
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
export default useFetch
|
||||
```
|
||||
|
||||
Now, let's combine everything and make it work.
|
||||
|
||||
```js
|
||||
// index.js
|
||||
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import axios from 'axios'
|
||||
import ReactDOM, { findDOMNode } from 'react-dom'
|
||||
import useFetch from './useFetch'
|
||||
|
||||
const Country = ({ country: { name, flag, population } }) => {
|
||||
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>
|
||||
<span>Population: </span>
|
||||
{population}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const App = (props) => {
|
||||
const url = 'https://restcountries.eu/rest/v2/all'
|
||||
const data = useFetch(url)
|
||||
|
||||
return (
|
||||
<div className='App'>
|
||||
<h1>Custom Hooks</h1>
|
||||
<h1>Calling API</h1>
|
||||
<div>
|
||||
<p>There are {data.length} countries in the api</p>
|
||||
<div className='countries-wrapper'>
|
||||
{data.map((country) => (
|
||||
<Country country={country} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
The useState and useEffect hooks are most common React hooks which you use on daily bases. In addition to the basic hook, there are additional hooks which are not used often. You do not have to know how to use all the hooks. The useState, useEffect and useRef are very important hooks and it is recommended to know how to use them.
|
||||
|
||||
# Exercises
|
||||
|
||||
Note: Continue building the countries application
|
||||
|
||||
1 Build the following application using [countries API](https://restcountries.eu/rest/v2/all).
|
||||
[DEMO](https://www.30daysofreact.com/day-23/countries-data)
|
||||
|
||||
🎉 CONGRATULATIONS ! 🎉
|
||||
|
||||
[<< Day 24](../24_projects/24_projects.md) | [Day 26>>](../26_Context/26_context.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 25
|
||||
|
||||
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,44 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import axios from 'axios'
|
||||
import ReactDOM, { findDOMNode } from 'react-dom'
|
||||
import useFetch from './useFetch'
|
||||
|
||||
const Country = ({ country: { name, flag, population } }) => {
|
||||
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>
|
||||
<span>Population: </span>
|
||||
{population}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const App = (props) => {
|
||||
const url = 'https://restcountries.eu/rest/v2/all'
|
||||
const data = useFetch(url)
|
||||
|
||||
return (
|
||||
<div className='App'>
|
||||
<h1>Custom Hooks</h1>
|
||||
<h1>Calling API</h1>
|
||||
<div>
|
||||
<p>There are {data.length} countries in the api</p>
|
||||
<div className='countries-wrapper'>
|
||||
{data.map((country) => (
|
||||
<Country country={country} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
@ -0,0 +1,86 @@
|
||||
/* == 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%;
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
export const useFetch = (url) => {
|
||||
const [data, setData] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
const data = await response.json()
|
||||
setData(data)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
fetchData()
|
||||
}, [url])
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
export default useFetch
|
@ -0,0 +1,38 @@
|
||||
<div align="center">
|
||||
<h1> 30 Days Of React: Context</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 25](../25_Custom_Hooks/25_custom_hooks.md) | [Day 27>>](../27_Ref/27_ref.md)
|
||||
|
||||

|
||||
|
||||
# Context
|
||||
|
||||
Context allow as to pass data through the component tree without having to pass props down manually to every child component at every level.
|
||||
|
||||
In React, data is passed top-down (parent to child) via props, but this can be cumbersome for certain types of props (e.g. locale preference, UI theme) that are required by many components within an application. Context provides a way to share values like these between components without having to explicitly pass a prop through every level of the tree.
|
||||
|
||||
## When to Use Context
|
||||
|
||||
Context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language. For example, in the code below we manually thread through a “theme” prop in order to style the Button component:
|
||||
|
||||
The above text has been taken from [react documentation](https://reactjs.org/docs/context.html) without any change.
|
||||
|
||||
It seems the react documentation has pretty good information about context, you can go through the [react documentation](https://reactjs.org/docs/context.html).
|
||||
|
||||
# Exercises
|
||||
|
||||
🎉 CONGRATULATIONS ! 🎉
|
||||
[<< Day 25](../25_Custom_Hooks/25_custom_hooks.md) | [Day 27>>](../27_Ref/27_ref.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 25
|
||||
|
||||
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,44 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import axios from 'axios'
|
||||
import ReactDOM, { findDOMNode } from 'react-dom'
|
||||
import useFetch from './useFetch'
|
||||
|
||||
const Country = ({ country: { name, flag, population } }) => {
|
||||
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>
|
||||
<span>Population: </span>
|
||||
{population}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const App = (props) => {
|
||||
const url = 'https://restcountries.eu/rest/v2/all'
|
||||
const data = useFetch(url)
|
||||
|
||||
return (
|
||||
<div className='App'>
|
||||
<h1>Custom Hooks</h1>
|
||||
<h1>Calling API</h1>
|
||||
<div>
|
||||
<p>There are {data.length} countries in the api</p>
|
||||
<div className='countries-wrapper'>
|
||||
{data.map((country) => (
|
||||
<Country country={country} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
@ -0,0 +1,86 @@
|
||||
/* == 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%;
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
export const useFetch = (url) => {
|
||||
const [data, setData] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
const data = await response.json()
|
||||
setData(data)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
fetchData()
|
||||
}, [url])
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
export default useFetch
|
@ -0,0 +1,144 @@
|
||||
<div align="center">
|
||||
<h1> 30 Days Of React: useRef</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 25](../25_Custom_Hooks/25_custom_hooks.md) | [Day 27>>]()
|
||||
|
||||

|
||||
|
||||
# useRef
|
||||
|
||||
In this challenge we have covered, how to handle uncontrolled input data. In this section, we will use the useRef hooks to get input data or to access any DOM element in your React application.
|
||||
|
||||
The useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.
|
||||
|
||||
In the following example, we see how to get data from input and access elements from the DOM tree using useRef hook.
|
||||
|
||||
## Getting Data from input
|
||||
|
||||
Let's get data from uncontrolled input element.
|
||||
|
||||
```js
|
||||
import React, { useRef } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
const App = (props) => {
|
||||
const ref = useRef(null)
|
||||
const onClick = () => {
|
||||
let value = ref.current.value
|
||||
alert(value)
|
||||
}
|
||||
return (
|
||||
<div className='App'>
|
||||
<h1>How to use data from uncontrolled input using useRef</h1>
|
||||
<input type='text' ref={ref} />
|
||||
<br />
|
||||
<button onClick={onClick}>Get Input Data</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
## Focus
|
||||
|
||||
Using the useRef we can trigger the focus event on input.
|
||||
|
||||
```js
|
||||
import React, { useRef } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
const App = (props) => {
|
||||
const ref = useRef(null)
|
||||
const onClick = () => {
|
||||
ref.current.focus()
|
||||
}
|
||||
return (
|
||||
<div className='App'>
|
||||
<h1>How to focus on input element useRef</h1>
|
||||
<input type='text' ref={ref} />
|
||||
<br />
|
||||
<button onClick={onClick}>Click to Focus on input</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
## Getting Content from DOM tree
|
||||
|
||||
Don't touch the DOM when you develop a React application because React has its own way to manipulate the DOM using the virtual DOM. In case, we get interested to get some content from the DOM tree we can use the useRef hook. See the example:
|
||||
|
||||
```js
|
||||
import React, { useRef } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
const App = (props) => {
|
||||
const ref = useRef(null)
|
||||
const onClick = () => {
|
||||
let content = ref.current.textContent
|
||||
alert(content)
|
||||
console.log(content)
|
||||
}
|
||||
return (
|
||||
<div className='App'>
|
||||
<h1 ref={ref}>How to getting content from the DOM tree</h1>
|
||||
<button onClick={onClick}>Getting Content</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
## Accessing and Styling a DOM element
|
||||
|
||||
We can access and style an element from the DOM tree. See the example below:
|
||||
|
||||
```js
|
||||
import React, { useRef } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
const App = (props) => {
|
||||
const ref = useRef(null)
|
||||
const onClick = () => {
|
||||
ref.current.style.backgroundColor = '#61dbfb'
|
||||
ref.current.style.padding = '50px'
|
||||
ref.current.style.textAlign = 'center'
|
||||
}
|
||||
return (
|
||||
<div className='App'>
|
||||
<h1 ref={ref}>How to style HTML from the DOM tree using useRef</h1>
|
||||
<button onClick={onClick}>Style it</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement
|
||||
```
|
||||
|
||||
# Exercises
|
||||
|
||||
1. Develop the following [application](https://www.30daysofreact.com/day-27/hexadecimal-colors). The application generates 27 hexadecimal colors by default. If the generate button get clicked it will generate another new 27 hexadecimal colors.
|
||||
|
||||
🎉 CONGRATULATIONS ! 🎉
|
||||
|
||||
[<< Day 25](../25_Custom_Hooks/25_custom_hooks.md) | [Day 27>>]()
|
@ -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 25
|
||||
|
||||
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,20 @@
|
||||
import React, { useRef } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
const App = (props) => {
|
||||
const ref = useRef(null)
|
||||
const onClick = () => {
|
||||
ref.current.style.backgroundColor = '#61dbfb'
|
||||
ref.current.style.padding = '50px'
|
||||
ref.current.style.textAlign = 'center'
|
||||
}
|
||||
return (
|
||||
<div className='App'>
|
||||
<h1 ref={ref}>How to style HTML from the DOM tree using useRef</h1>
|
||||
<button onClick={onClick}>Style it</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
@ -0,0 +1,86 @@
|
||||
/* == 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%;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
export const useFetch = (url) => {
|
||||
const [data, setData] = useState([])
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
const data = await response.json()
|
||||
setData(data)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
fetchData()
|
||||
}, [url])
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
export default useFetch
|
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 |