day 23 has been added

pull/72/head
asabeneh 4 years ago
parent 34e4ef9d00
commit 06364e1901

@ -14,7 +14,7 @@
</div>
[<< Day 21](../21_Introducing_Hooks/21_introducing_hooks.md) | [Day 23>>]()
[<< Day 21](../21_Introducing_Hooks/21_introducing_hooks.md) | [Day 23>>](../23_Fetching_Data_Using_Hooks/23_fetching_data_using_hooks.md)
![30 Days of React banner](../images/30_days_of_react_banner_day_22.jpg)
@ -969,4 +969,5 @@ ReactDOM.render(<App />, rootElement)
Coming ..
🎉 CONGRATULATIONS ! 🎉
[<< Day 21](../21_Introducing_Hooks/21_introducing_hooks.md) | [Day 23>>]()
[<< Day 21](../21_Introducing_Hooks/21_introducing_hooks.md) | [Day 23>>](../23_Fetching_Data_Using_Hooks/23_fetching_data_using_hooks.md)

@ -0,0 +1,122 @@
<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>>]()
![30 Days of React banner](../images/30_days_of_react_banner_day_23.jpg)
- [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, capital, flag, languages, population, currency },
}) => {
const formattedCapital =
capital.length > 0 ? (
<>
<span>Capital: </span>
{capital}
</>
) : (
''
)
const formattedLanguage = languages.length > 1 ? `Languages` : `Language`
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>{formattedCapital}</p>
<p>
<span>{formattedLanguage}: </span>
{languages.map((language) => language.name).join(', ')}
</p>
<p>
<span>Population: </span>
{population.toLocaleString()}
</p>
<p>
<span>Currency: </span>
{currency}
</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>>]()

@ -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 },
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

@ -0,0 +1,365 @@
import React, { useState } from 'react'
import ReactDOM from 'react-dom'
const options = [
{
value: '',
label: '-- Select Country--',
},
{
value: 'Finland',
label: 'Finland',
},
{
value: 'Sweden',
label: 'Sweden',
},
{
value: 'Norway',
label: 'Norway',
},
{
value: 'Denmark',
label: 'Denmark',
},
]
// mapping the options to list(array) of JSX options
const selectOptions = options.map(({ value, label }) => (
<option key={label} value={value}>
{' '}
{label}
</option>
))
const App = (props) => {
const initialState = {
firstName: '',
lastName: '',
email: '',
title: '',
country: '',
tel: '',
dateOfBirth: '',
favoriteColor: '',
weight: '',
gender: '',
file: '',
bio: '',
skills: {
html: false,
css: false,
javascript: false,
},
touched: {
firstName: false,
lastName: false,
},
}
const [formData, setFormData] = useState(initialState)
const onChange = (e) => {
/*
we can get the name and value like: e.target.name, e.target.value
Wwe can also destructure name and value from e.target
const name = e.target.name
const value = e.target.value
*/
const { name, value, type, checked } = e.target
/*
[variablename] we can make a value stored in a certain variable could be a key for an object, in this case a key for the state
*/
if (type === 'checkbox') {
setFormData({
...formData,
skills: { ...formData.skills, [name]: checked },
})
} else if (type === 'file') {
setFormData({ ...formData, [name]: e.target.files[0] })
} else {
setFormData({ ...formData, [name]: value })
}
}
const onSubmit = (e) => {
/*
e.preventDefault()
stops the default behavior of form element
specifically refreshing of page
*/
e.preventDefault()
const {
firstName,
lastName,
title,
email,
tel,
dateOfBirth,
favoriteColor,
weight,
country,
gender,
bio,
file,
skills,
} = formData
const formattedSkills = []
for (const key in skills) {
console.log(key)
if (skills[key]) {
formattedSkills.push(key.toUpperCase())
}
}
const data = {
firstName,
lastName,
title,
email,
tel,
dateOfBirth,
favoriteColor,
weight,
country,
gender,
bio,
file,
skills: formattedSkills,
}
/*
the is the place where we connect backend api
to send the data to the database
*/
console.log(data)
}
const onBlur = (e) => {
const { name } = e.target
setFormData({ ...formData, touched: { ...formData.touched, [name]: true } })
}
const validate = () => {
// Object to collect error feedback and to display on the form
const errors = {
firstName: '',
}
if (
(formData.touched.firstName && formData.firstName.length < 3) ||
(formData.touched.firstName && formData.firstName.length > 12)
) {
errors.firstName = 'First name must be between 2 and 12'
}
return errors
}
// accessing the state value by destrutcturing the state
const {
firstName,
lastName,
title,
country,
email,
tel,
dateOfBirth,
favoriteColor,
weight,
gender,
bio,
} = formData
const errors = validate()
return (
<div className='App'>
<h3>Add Student</h3>
<form onSubmit={onSubmit}>
<div className='row'>
<div className='form-group'>
<label htmlFor='firstName'>First Name </label>
<input
type='text'
id='firstName'
name='firstName'
value={firstName}
onChange={onChange}
onBlur={onBlur}
placeholder='First Name'
/>
<br />
{errors.firstName && <small>{errors.firstName}</small>}
</div>
<div className='form-group'>
<label htmlFor='lastName'>Last Name </label>
<input
type='text'
id='lastName'
name='lastName'
value={lastName}
onChange={onChange}
placeholder='Last Name'
/>
</div>
<div className='form-group'>
<label htmlFor='title'>Title </label>
<input
type='text'
id='title'
name='title'
placeholder='Title'
value={title}
onChange={onChange}
/>
</div>
<div className='form-group'>
<label htmlFor='email'>Email </label>
<input
type='email'
id='email'
name='email'
value={email}
onChange={onChange}
placeholder='Email'
/>
</div>
</div>
<div className='form-group'>
<label htmlFor='tel'>Telephone </label>
<input
type='tel'
id='tel'
name='tel'
value={tel}
onChange={onChange}
placeholder='Tel'
/>
</div>
<div className='form-group'>
<label htmlFor='dateOfBirth'>Date of birth </label>
<input
type='date'
id='dateOfBirth'
name='dateOfBirth'
value={dateOfBirth}
onChange={onChange}
placeholder='Date of Birth'
/>
</div>
<div className='form-group'>
<label htmlFor='favoriteColor'>Favorite Color</label>
<input
type='color'
id='color'
name='favoriteColor'
value={favoriteColor}
onChange={onChange}
placeholder='Favorite Color'
/>
</div>
<div className='form-group'>
<label htmlFor='weight'>Weight </label>
<input
type='number'
id='weight'
name='weight'
value={weight}
onChange={onChange}
placeholder='Weight in Kg'
/>
</div>
<div>
<label htmlFor='country'>Country</label> <br />
<select
name='country'
onChange={onChange}
id='country'
value={country}
>
{selectOptions}
</select>
</div>
<div>
<p>Gender</p>
<div>
<input
type='radio'
id='female'
name='gender'
value='Female'
onChange={onChange}
checked={gender === 'Female'}
/>
<label htmlFor='female'>Female</label>
</div>
<div>
<input
id='male'
type='radio'
name='gender'
value='Male'
onChange={onChange}
checked={gender === 'Male'}
/>
<label htmlFor='male'>Male</label>
</div>
<div>
<input
id='other'
type='radio'
name='gender'
value='Other'
onChange={onChange}
checked={gender === 'Other'}
/>
<label htmlFor='other'>Other</label>
</div>
</div>
<div>
<p>Select your skills</p>
<div>
<input type='checkbox' id='html' name='html' onChange={onChange} />
<label htmlFor='html'>HTML</label>
</div>
<div>
<input type='checkbox' id='css' name='css' onChange={onChange} />
<label htmlFor='css'>CSS</label>
</div>
<div>
<input
type='checkbox'
id='javascript'
name='javascript'
onChange={onChange}
/>
<label htmlFor='javascript'>JavaScript</label>
</div>
</div>
<div>
<label htmlFor='bio'>Bio</label> <br />
<textarea
id='bio'
name='bio'
value={bio}
onChange={onChange}
cols='120'
rows='10'
placeholder='Write about yourself ...'
/>
</div>
<div>
<input type='file' name='file' onChange={onChange} />
</div>
<div>
<button>Submit</button>
</div>
</form>
</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%;
}

@ -45,6 +45,7 @@
|20|[Projects](./20_projects/20_projects.md)|
|21|[Hooks](./21_Introducing_Hooks/21_introducing_hooks.md)|
|22|[Forms Using Hook](./22_Form_Using_Hooks/22_form_using_hooks.md)|
|23|[Fetching Data Using Hooks](./23_Fetching_Data_Using_Hooks/23_fetching_data_using_hooks.md)|

Loading…
Cancel
Save