@ -1,7 +1,5 @@
|
||||
draft.md
|
||||
react-for-everyone.md
|
||||
component.md
|
||||
09_Day_Conditional_Rendering
|
||||
10_Day_Events
|
||||
11_Day_Forms
|
||||
|
||||
|
||||
|
@ -0,0 +1,780 @@
|
||||
<div align="center">
|
||||
<h1> 30 Days Of React: Conditional Rendering</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 8](../08_Day_States/08_states.md) | [Day 10 >>](../10_Day_Events/10_events.md)
|
||||
|
||||

|
||||
|
||||
# Conditional Rendering
|
||||
|
||||
As we can understand from the term, conditional rendering is a way to render different JSX or component at different condition. We can implement conditional rendering using regular if and else statement, ternary operator and &&. Let's implement a different conditional rendering.
|
||||
|
||||
## Conditional Rendering using If and Else statement
|
||||
|
||||
In the code below, we have an initial state of loggedIn which is false. If the state is false we inform user to log in otherwise we welcome the user.
|
||||
|
||||
```js
|
||||
// index.js
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
// class based component
|
||||
class Header extends React.Component {
|
||||
render() {
|
||||
const {
|
||||
welcome,
|
||||
title,
|
||||
subtitle,
|
||||
author: { firstName, lastName },
|
||||
date,
|
||||
} = this.props.data
|
||||
|
||||
return (
|
||||
<header>
|
||||
<div className='header-wrapper'>
|
||||
<h1>{welcome}</h1>
|
||||
<h2>{title}</h2>
|
||||
<h3>{subtitle}</h3>
|
||||
<p>
|
||||
{firstName} {lastName}
|
||||
</p>
|
||||
<small>{date}</small>
|
||||
<p>Select a country for your next holiday</p>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class App extends React.Component {
|
||||
state = {
|
||||
loggedIn: false,
|
||||
}
|
||||
|
||||
render() {
|
||||
const data = {
|
||||
welcome: '30 Days Of React',
|
||||
title: 'Getting Started React',
|
||||
subtitle: 'JavaScript Library',
|
||||
author: {
|
||||
firstName: 'Asabeneh',
|
||||
lastName: 'Yetayeh',
|
||||
},
|
||||
date: 'Oct 9, 2020',
|
||||
}
|
||||
|
||||
// conditional rendering using if and else statement
|
||||
|
||||
let status
|
||||
|
||||
if (this.state.loggedIn) {
|
||||
status = <h3>Welcome to 30 Days Of React</h3>
|
||||
} else {
|
||||
status = <h3>Please Login</h3>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='app'>
|
||||
<Header data={data} />
|
||||
{status}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
Let's add a method which allow as to toggle the status of the user. We should have a button to handle event for logging in and logging out.
|
||||
|
||||
```js
|
||||
// index.js
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
// A button component
|
||||
const Button = ({ text, onClick, style }) => (
|
||||
<button style={style} onClick={onClick}>
|
||||
{text}
|
||||
</button>
|
||||
)
|
||||
|
||||
// CSS styles in JavaScript Object
|
||||
const buttonStyles = {
|
||||
backgroundColor: '#61dbfb',
|
||||
padding: 10,
|
||||
border: 'none',
|
||||
borderRadius: 5,
|
||||
margin: '3px auto',
|
||||
cursor: 'pointer',
|
||||
fontSize: 22,
|
||||
color: 'white',
|
||||
}
|
||||
|
||||
// class based component
|
||||
class Header extends React.Component {
|
||||
render() {
|
||||
console.log(this.props.data)
|
||||
const {
|
||||
welcome,
|
||||
title,
|
||||
subtitle,
|
||||
author: { firstName, lastName },
|
||||
date,
|
||||
} = this.props.data
|
||||
|
||||
return (
|
||||
<header>
|
||||
<div className='header-wrapper'>
|
||||
<h1>{welcome}</h1>
|
||||
<h2>{title}</h2>
|
||||
<h3>{subtitle}</h3>
|
||||
<p>
|
||||
{firstName} {lastName}
|
||||
</p>
|
||||
<small>{date}</small>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class App extends React.Component {
|
||||
state = {
|
||||
loggedIn: false,
|
||||
}
|
||||
handleLogin = () => {
|
||||
this.setState({
|
||||
loggedIn: !this.state.loggedIn,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const data = {
|
||||
welcome: '30 Days Of React',
|
||||
title: 'Getting Started React',
|
||||
subtitle: 'JavaScript Library',
|
||||
author: {
|
||||
firstName: 'Asabeneh',
|
||||
lastName: 'Yetayeh',
|
||||
},
|
||||
date: 'Oct 9, 2020',
|
||||
}
|
||||
|
||||
let status
|
||||
let text
|
||||
|
||||
if (this.state.loggedIn) {
|
||||
status = <h1>Welcome to 30 Days Of React</h1>
|
||||
text = 'Logout'
|
||||
} else {
|
||||
status = <h3>Please Login</h3>
|
||||
text = 'Login'
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='app'>
|
||||
<Header data={data} />
|
||||
{status}
|
||||
<Button text={text} style={buttonStyles} onClick={this.handleLogin} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
How about if our condition is more than two? Like pure JavaScript we can use if else if statement. In general, conditional rendering is not different from pure JavaScript conditional statement.
|
||||
|
||||
## Conditional Rendering using Ternary Operator
|
||||
|
||||
Ternary operator is an an alternative for if else statement. However, there is more use cases for ternary operator than if else statement. For example, use can use ternary operator inside styles, className or many places in a component than regular if else statement.
|
||||
|
||||
```js
|
||||
// index.js
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
// A button component
|
||||
const Button = ({ text, onClick, style }) => (
|
||||
<button style={style} onClick={onClick}>
|
||||
{text}
|
||||
</button>
|
||||
)
|
||||
|
||||
// CSS styles in JavaScript Object
|
||||
const buttonStyles = {
|
||||
backgroundColor: '#61dbfb',
|
||||
padding: 10,
|
||||
border: 'none',
|
||||
borderRadius: 5,
|
||||
margin: '3px auto',
|
||||
cursor: 'pointer',
|
||||
fontSize: 22,
|
||||
color: 'white',
|
||||
}
|
||||
|
||||
// class based component
|
||||
class Header extends React.Component {
|
||||
render() {
|
||||
const {
|
||||
welcome,
|
||||
title,
|
||||
subtitle,
|
||||
author: { firstName, lastName },
|
||||
date,
|
||||
} = this.props.data
|
||||
|
||||
return (
|
||||
<header>
|
||||
<div className='header-wrapper'>
|
||||
<h1>{welcome}</h1>
|
||||
<h2>{title}</h2>
|
||||
<h3>{subtitle}</h3>
|
||||
<p>
|
||||
{firstName} {lastName}
|
||||
</p>
|
||||
<small>{date}</small>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class App extends React.Component {
|
||||
state = {
|
||||
loggedIn: false,
|
||||
}
|
||||
handleLogin = () => {
|
||||
this.setState({
|
||||
loggedIn: !this.state.loggedIn,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const data = {
|
||||
welcome: '30 Days Of React',
|
||||
title: 'Getting Started React',
|
||||
subtitle: 'JavaScript Library',
|
||||
author: {
|
||||
firstName: 'Asabeneh',
|
||||
lastName: 'Yetayeh',
|
||||
},
|
||||
date: 'Oct 9, 2020',
|
||||
}
|
||||
|
||||
let status = this.state.loggedIn ? (
|
||||
<h1>Welcome to 30 Days Of React</h1>
|
||||
) : (
|
||||
<h3>Please Login</h3>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className='app'>
|
||||
<Header data={data} />
|
||||
{status}
|
||||
<Button
|
||||
text={this.state.loggedIn ? 'Logout' : 'Login'}
|
||||
style={buttonStyles}
|
||||
onClick={this.handleLogin}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
In addition to JSX, we can also conditionally render a component. Let's change the above conditional JSX to a component.
|
||||
|
||||
```js
|
||||
// index.js
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
// A button component
|
||||
const Button = ({ text, onClick, style }) => (
|
||||
<button style={style} onClick={onClick}>
|
||||
{text}
|
||||
</button>
|
||||
)
|
||||
|
||||
// CSS styles in JavaScript Object
|
||||
const buttonStyles = {
|
||||
backgroundColor: '#61dbfb',
|
||||
padding: 10,
|
||||
border: 'none',
|
||||
borderRadius: 5,
|
||||
margin: '3px auto',
|
||||
cursor: 'pointer',
|
||||
fontSize: 22,
|
||||
color: 'white',
|
||||
}
|
||||
|
||||
// class based component
|
||||
class Header extends React.Component {
|
||||
render() {
|
||||
const {
|
||||
welcome,
|
||||
title,
|
||||
subtitle,
|
||||
author: { firstName, lastName },
|
||||
date,
|
||||
} = this.props.data
|
||||
|
||||
return (
|
||||
<header>
|
||||
<div className='header-wrapper'>
|
||||
<h1>{welcome}</h1>
|
||||
<h2>{title}</h2>
|
||||
<h3>{subtitle}</h3>
|
||||
<p>
|
||||
{firstName} {lastName}
|
||||
</p>
|
||||
<small>{date}</small>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const Login = () => (
|
||||
<div>
|
||||
<h3>Please Login</h3>
|
||||
</div>
|
||||
)
|
||||
const Welcome = (props) => (
|
||||
<div>
|
||||
<h1>Welcome to 30 Days Of React</h1>
|
||||
</div>
|
||||
)
|
||||
|
||||
class App extends React.Component {
|
||||
state = {
|
||||
loggedIn: false,
|
||||
}
|
||||
handleLogin = () => {
|
||||
this.setState({
|
||||
loggedIn: !this.state.loggedIn,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const data = {
|
||||
welcome: '30 Days Of React',
|
||||
title: 'Getting Started React',
|
||||
subtitle: 'JavaScript Library',
|
||||
author: {
|
||||
firstName: 'Asabeneh',
|
||||
lastName: 'Yetayeh',
|
||||
},
|
||||
date: 'Oct 9, 2020',
|
||||
}
|
||||
|
||||
const status = this.state.loggedIn ? <Welcome /> : <Login />
|
||||
|
||||
return (
|
||||
<div className='app'>
|
||||
<Header data={data} />
|
||||
{status}
|
||||
<Button
|
||||
text={this.state.loggedIn ? 'Logout' : 'Login'}
|
||||
style={buttonStyles}
|
||||
onClick={this.handleLogin}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
## Conditional Rendering using && Operator
|
||||
|
||||
The && operator render the right JSX operand if the left operand(expression) is true.
|
||||
|
||||
```js
|
||||
// index.js
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
// A button component
|
||||
const Button = ({ text, onClick, style }) => (
|
||||
<button style={style} onClick={onClick}>
|
||||
{text}
|
||||
</button>
|
||||
)
|
||||
|
||||
// CSS styles in JavaScript Object
|
||||
const buttonStyles = {
|
||||
backgroundColor: '#61dbfb',
|
||||
padding: 10,
|
||||
border: 'none',
|
||||
borderRadius: 5,
|
||||
margin: '3px auto',
|
||||
cursor: 'pointer',
|
||||
fontSize: 22,
|
||||
color: 'white',
|
||||
}
|
||||
|
||||
// class based component
|
||||
class Header extends React.Component {
|
||||
render() {
|
||||
console.log(this.props.data)
|
||||
const {
|
||||
welcome,
|
||||
title,
|
||||
subtitle,
|
||||
author: { firstName, lastName },
|
||||
date,
|
||||
} = this.props.data
|
||||
|
||||
return (
|
||||
<header style={this.props.styles}>
|
||||
<div className='header-wrapper'>
|
||||
<h1>{welcome}</h1>
|
||||
<h2>{title}</h2>
|
||||
<h3>{subtitle}</h3>
|
||||
<p>
|
||||
{firstName} {lastName}
|
||||
</p>
|
||||
<small>{date}</small>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
}
|
||||
const Login = () => (
|
||||
<div>
|
||||
<h3>Please Login</h3>
|
||||
</div>
|
||||
)
|
||||
const Welcome = (props) => (
|
||||
<div>
|
||||
<h1>Welcome to 30 Days Of React</h1>
|
||||
</div>
|
||||
)
|
||||
|
||||
class App extends React.Component {
|
||||
state = {
|
||||
loggedIn: false,
|
||||
techs: ['HTML', 'CSS', 'JS'],
|
||||
}
|
||||
handleLogin = () => {
|
||||
this.setState({
|
||||
loggedIn: !this.state.loggedIn,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const data = {
|
||||
welcome: '30 Days Of React',
|
||||
title: 'Getting Started React',
|
||||
subtitle: 'JavaScript Library',
|
||||
author: {
|
||||
firstName: 'Asabeneh',
|
||||
lastName: 'Yetayeh',
|
||||
},
|
||||
date: 'Oct 9, 2020',
|
||||
}
|
||||
|
||||
// We can destructure state
|
||||
|
||||
const { loggedIn, techs } = this.state
|
||||
|
||||
const status = loggedIn ? <Welcome /> : <Login />
|
||||
|
||||
return (
|
||||
<div className='app'>
|
||||
<Header data={data} />
|
||||
{status}
|
||||
<Button
|
||||
text={loggedIn ? 'Logout' : 'Login'}
|
||||
style={buttonStyles}
|
||||
onClick={this.handleLogin}
|
||||
/>
|
||||
{techs.length === 3 && (
|
||||
<p>You have all the prerequisite courses to get started React</p>
|
||||
)}
|
||||
{!loggedIn && (
|
||||
<p>
|
||||
Please login to access more information about 30 Days Of React
|
||||
challenge
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
In the previous section, we used alert box to greet people and also to display time. Let's render the greeting and time on browser DOM instead of displaying on alert box.
|
||||
|
||||
```js
|
||||
// index.js
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
// class based component
|
||||
class Header extends React.Component {
|
||||
render() {
|
||||
console.log(this.props.data)
|
||||
const {
|
||||
welcome,
|
||||
title,
|
||||
subtitle,
|
||||
author: { firstName, lastName },
|
||||
date,
|
||||
} = this.props.data
|
||||
|
||||
return (
|
||||
<header style={this.props.styles}>
|
||||
<div className='header-wrapper'>
|
||||
<h1>{welcome}</h1>
|
||||
<h2>{title}</h2>
|
||||
<h3>{subtitle}</h3>
|
||||
<p>
|
||||
{firstName} {lastName}
|
||||
</p>
|
||||
<small>{date}</small>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const Message = ({ message }) => (
|
||||
<div>
|
||||
<h1>{message}</h1>
|
||||
</div>
|
||||
)
|
||||
const Login = () => (
|
||||
<div>
|
||||
<h3>Please Login</h3>
|
||||
</div>
|
||||
)
|
||||
const Welcome = (props) => (
|
||||
<div>
|
||||
<h1>Welcome to 30 Days Of React</h1>
|
||||
</div>
|
||||
)
|
||||
|
||||
// A button component
|
||||
const Button = ({ text, onClick, style }) => (
|
||||
<button style={style} onClick={onClick}>
|
||||
{text}
|
||||
</button>
|
||||
)
|
||||
|
||||
// TechList Component
|
||||
// class base component
|
||||
class TechList extends React.Component {
|
||||
render() {
|
||||
const { techs } = this.props
|
||||
const techsFormatted = techs.map((tech) => <li key={tech}>{tech}</li>)
|
||||
return techsFormatted
|
||||
}
|
||||
}
|
||||
|
||||
// Main Component
|
||||
// Class Component
|
||||
class Main extends React.Component {
|
||||
render() {
|
||||
const {
|
||||
techs,
|
||||
greetPeople,
|
||||
handleTime,
|
||||
loggedIn,
|
||||
handleLogin,
|
||||
message,
|
||||
} = this.props
|
||||
console.log(message)
|
||||
|
||||
const status = loggedIn ? <Welcome /> : <Login />
|
||||
return (
|
||||
<main>
|
||||
<div className='main-wrapper'>
|
||||
<p>Prerequisite to get started react.js:</p>
|
||||
<ul>
|
||||
<TechList techs={this.props.techs} />
|
||||
</ul>
|
||||
{techs.length === 3 && (
|
||||
<p>You have all the prerequisite courses to get started React</p>
|
||||
)}
|
||||
<div>
|
||||
<Button
|
||||
text='Show Time'
|
||||
onClick={handleTime}
|
||||
style={buttonStyles}
|
||||
/>{' '}
|
||||
<Button
|
||||
text='Greet People'
|
||||
onClick={greetPeople}
|
||||
style={buttonStyles}
|
||||
/>
|
||||
{!loggedIn && <p>Please login to access more information about 30 Days Of React challenge</p>}
|
||||
</div>
|
||||
<div style={{ margin: 30 }}>
|
||||
<Button
|
||||
text={loggedIn ? 'Logout' : 'Login'}
|
||||
style={buttonStyles}
|
||||
onClick={handleLogin}
|
||||
/>
|
||||
<br />
|
||||
{status}
|
||||
</div>
|
||||
<Message message={message} />
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// CSS styles in JavaScript Object
|
||||
const buttonStyles = {
|
||||
backgroundColor: '#61dbfb',
|
||||
padding: 10,
|
||||
border: 'none',
|
||||
borderRadius: 5,
|
||||
margin: '3px auto',
|
||||
cursor: 'pointer',
|
||||
fontSize: 22,
|
||||
color: 'white',
|
||||
}
|
||||
|
||||
// Footer Component
|
||||
// Class component
|
||||
class Footer extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<footer>
|
||||
<div className='footer-wrapper'>
|
||||
<p>Copyright {this.props.date.getFullYear()}</p>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class App extends React.Component {
|
||||
state = {
|
||||
loggedIn: false,
|
||||
techs: ['HTML', 'CSS', 'JS'],
|
||||
message: 'Click show time or Greet people to change me',
|
||||
}
|
||||
handleLogin = () => {
|
||||
this.setState({
|
||||
loggedIn: !this.state.loggedIn,
|
||||
})
|
||||
}
|
||||
showDate = (time) => {
|
||||
const months = [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December',
|
||||
]
|
||||
|
||||
const month = months[time.getMonth()].slice(0, 3)
|
||||
const year = time.getFullYear()
|
||||
const date = time.getDate()
|
||||
return `${month} ${date}, ${year}`
|
||||
}
|
||||
handleTime = () => {
|
||||
let message = this.showDate(new Date())
|
||||
this.setState({ message })
|
||||
}
|
||||
greetPeople = () => {
|
||||
let message = 'Welcome to 30 Days Of React Challenge, 2020'
|
||||
this.setState({ message })
|
||||
}
|
||||
|
||||
render() {
|
||||
const data = {
|
||||
welcome: '30 Days Of React',
|
||||
title: 'Getting Started React',
|
||||
subtitle: 'JavaScript Library',
|
||||
author: {
|
||||
firstName: 'Asabeneh',
|
||||
lastName: 'Yetayeh',
|
||||
},
|
||||
date: 'Oct 9, 2020',
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='app'>
|
||||
<Header data={data} />
|
||||
|
||||
<Main
|
||||
techs={techs}
|
||||
handleTime={this.handleTime}
|
||||
greetPeople={this.greetPeople}
|
||||
loggedIn={this.state.loggedIn}
|
||||
handleLogin={this.handleLogin}
|
||||
message={this.state.message}
|
||||
/>
|
||||
|
||||
<Footer date={new Date()} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
## Exercises
|
||||
|
||||
### Exercises: Level 1
|
||||
|
||||
1. What is conditional rendering?
|
||||
2. How do you implement conditional rendering?
|
||||
3. Which method of conditional rendering do you prefer?
|
||||
|
||||
### Exercises: Level 2
|
||||
|
||||
1. Make a single page application which changes the body of the background based on the time of the day(Autumn, Winter, Spring, Summer)
|
||||
2. Make a single page application which change the body of the background based on the time of the day(Morning, Noon, Evening, Night)
|
||||
|
||||
### Exercises: Level 3
|
||||
|
||||
Coming
|
||||
|
||||
🎉 CONGRATULATIONS ! 🎉
|
||||
|
||||
[<< Day 8](../08_Day_States/08_states.md) | [Day 10 >>](../10_Day_Events/10_events.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 9
|
||||
|
||||
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,227 @@
|
||||
// index.js
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
// class based component
|
||||
class Header extends React.Component {
|
||||
render() {
|
||||
const {
|
||||
welcome,
|
||||
title,
|
||||
subtitle,
|
||||
author: { firstName, lastName },
|
||||
date,
|
||||
} = this.props.data
|
||||
|
||||
return (
|
||||
<header>
|
||||
<div className='header-wrapper'>
|
||||
<h1>{welcome}</h1>
|
||||
<h2>{title}</h2>
|
||||
<h3>{subtitle}</h3>
|
||||
<p>
|
||||
{firstName} {lastName}
|
||||
</p>
|
||||
<small>{date}</small>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const Message = ({ message }) => (
|
||||
<div>
|
||||
<h1>{message}</h1>
|
||||
</div>
|
||||
)
|
||||
const Login = () => (
|
||||
<div>
|
||||
<h3>Please Login</h3>
|
||||
</div>
|
||||
)
|
||||
const Welcome = (props) => (
|
||||
<div>
|
||||
<h1>Welcome to 30 Days Of React</h1>
|
||||
</div>
|
||||
)
|
||||
|
||||
// A button component
|
||||
const Button = ({ text, onClick, style }) => (
|
||||
<button style={style} onClick={onClick}>
|
||||
{text}
|
||||
</button>
|
||||
)
|
||||
|
||||
// TechList Component
|
||||
// class base component
|
||||
class TechList extends React.Component {
|
||||
render() {
|
||||
const { techs } = this.props
|
||||
const techsFormatted = techs.map((tech) => <li key={tech}>{tech}</li>)
|
||||
return techsFormatted
|
||||
}
|
||||
}
|
||||
|
||||
// Main Component
|
||||
// Class Component
|
||||
class Main extends React.Component {
|
||||
render() {
|
||||
const {
|
||||
techs,
|
||||
greetPeople,
|
||||
handleTime,
|
||||
loggedIn,
|
||||
handleLogin,
|
||||
message,
|
||||
} = this.props
|
||||
console.log(message)
|
||||
|
||||
const status = loggedIn ? <Welcome /> : <Login />
|
||||
return (
|
||||
<main>
|
||||
<div className='main-wrapper'>
|
||||
<p>Prerequisite to get started react.js:</p>
|
||||
<ul>
|
||||
<TechList techs={this.props.techs} />
|
||||
</ul>
|
||||
{techs.length === 3 && (
|
||||
<p>You have all the prerequisite courses to get started React</p>
|
||||
)}
|
||||
<div>
|
||||
<Button
|
||||
text='Show Time'
|
||||
onClick={handleTime}
|
||||
style={buttonStyles}
|
||||
/>{' '}
|
||||
<Button
|
||||
text='Greet People'
|
||||
onClick={greetPeople}
|
||||
style={buttonStyles}
|
||||
/>
|
||||
{!loggedIn && (
|
||||
<p>
|
||||
Please login to access more information about 30 Days Of React
|
||||
challenge
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ margin: 30 }}>
|
||||
<Button
|
||||
text={loggedIn ? 'Logout' : 'Login'}
|
||||
style={buttonStyles}
|
||||
onClick={handleLogin}
|
||||
/>
|
||||
<br />
|
||||
{status}
|
||||
</div>
|
||||
<Message message={message} />
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// CSS styles in JavaScript Object
|
||||
const buttonStyles = {
|
||||
backgroundColor: '#61dbfb',
|
||||
padding: 10,
|
||||
border: 'none',
|
||||
borderRadius: 5,
|
||||
margin: '3px auto',
|
||||
cursor: 'pointer',
|
||||
fontSize: 22,
|
||||
color: 'white',
|
||||
}
|
||||
|
||||
// Footer Component
|
||||
// Class component
|
||||
class Footer extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<footer>
|
||||
<div className='footer-wrapper'>
|
||||
<p>Copyright {this.props.date.getFullYear()}</p>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class App extends React.Component {
|
||||
state = {
|
||||
loggedIn: false,
|
||||
techs: ['HTML', 'CSS', 'JS'],
|
||||
message: 'Click show time or Greet people to change me',
|
||||
}
|
||||
handleLogin = () => {
|
||||
this.setState({
|
||||
loggedIn: !this.state.loggedIn,
|
||||
})
|
||||
}
|
||||
showDate = (time) => {
|
||||
const months = [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December',
|
||||
]
|
||||
|
||||
const month = months[time.getMonth()].slice(0, 3)
|
||||
const year = time.getFullYear()
|
||||
const date = time.getDate()
|
||||
return `${month} ${date}, ${year}`
|
||||
}
|
||||
handleTime = () => {
|
||||
let message = this.showDate(new Date())
|
||||
this.setState({ message })
|
||||
}
|
||||
greetPeople = () => {
|
||||
let message = 'Welcome to 30 Days Of React Challenge, 2020'
|
||||
this.setState({ message })
|
||||
}
|
||||
|
||||
render() {
|
||||
const data = {
|
||||
welcome: '30 Days Of React',
|
||||
title: 'Getting Started React',
|
||||
subtitle: 'JavaScript Library',
|
||||
author: {
|
||||
firstName: 'Asabeneh',
|
||||
lastName: 'Yetayeh',
|
||||
},
|
||||
date: 'Oct 9, 2020',
|
||||
}
|
||||
const techs = ['HTML', 'CSS', 'JavaScript']
|
||||
|
||||
return (
|
||||
<div className='app'>
|
||||
<Header data={data} />
|
||||
|
||||
<Main
|
||||
techs={techs}
|
||||
handleTime={this.handleTime}
|
||||
greetPeople={this.greetPeople}
|
||||
loggedIn={this.state.loggedIn}
|
||||
handleLogin={this.handleLogin}
|
||||
message={this.state.message}
|
||||
/>
|
||||
|
||||
<Footer date={new Date()} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
@ -0,0 +1,618 @@
|
||||
<div align="center">
|
||||
<h1> 30 Days Of React: Statet</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 9](../09_Day_Conditional_Rendering/09_conditional_rendering.md) | [Day 11 >>](../11_Day_Events/10_events.md)
|
||||
|
||||

|
||||
|
||||
- [React Project Folder Structure and File Naming](#react-project-folder-structure-and-file-naming)
|
||||
- [File Naming](#file-naming)
|
||||
- [Folder](#folder)
|
||||
- [Components Folder](#components-folder)
|
||||
- [Fragments](#fragments)
|
||||
- [Exercises](#exercises)
|
||||
- [Exercises:Level 1](#exerciseslevel-1)
|
||||
- [Exercises:Level 2](#exerciseslevel-2)
|
||||
|
||||
# React Project Folder Structure and File Naming
|
||||
|
||||
There is no strict way to use a single folder structure or file naming in React project. Most of the time, these kind of choice can be made by a team. Sometimes a company may have a developed guidelines about what code conventions to follow, folder structure and file naming. There is no right or wrong way of structuring a React project but some structures are better than the others for scalability,maintainability, ease of working on files and easy to understand structure. If you like to learn further about folder structure you may check the following articles.
|
||||
|
||||
- [React Folder Structure by https://www.devaradise.com ](https://www.devaradise.com/react-project-folder-structure)
|
||||
- [React Folder Structure by www.robinwieruch.de ](https://www.robinwieruch.de/react-folder-structure)
|
||||
- [React Folder Structure by Faraz Ahmad](https://dev.to/farazamiruddin/an-opinionated-guide-to-react-folder-structure-file-naming-1l7i)
|
||||
- [React Folder Structure by https://maxrozen.com/](https://maxrozen.com/guidelines-improve-react-app-folder-structure/)
|
||||
|
||||
I use a mix of different conventions. If you like you can follow it but please stick in a structure which you think makes sense for you.
|
||||
|
||||
## File Naming
|
||||
|
||||
In all my React project, I will use CamelCase file name for all components. I prefer to use descriptive long name.
|
||||
|
||||
## Folder
|
||||
|
||||
I found it easy to put all images, icons and fonts in the assets folder and all CSS style sheets in styles folder. All components will be in the components folder.
|
||||
|
||||
So far, we have been working on index.js file. We have lots of component on index.js. Today we will move every component to a single file and we will import all the files to App.js. In the process, you will see my folder structure. Currently, we are at src directory. All the folder structure will be inside the src directory. Let's start from the index.js file. In addition to index.js file, let's create an App.js file and move most of the components we had to App.js for the time being.
|
||||
The index.js is your getaway to connect the component with index.html.
|
||||
|
||||
```js
|
||||
// src/index.js
|
||||
// index.js
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
const App = () => <h1>Welcome to 30 Days Of React</h1>
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
In the above snippet of code, we have the App component. Let's create the App component to its own file, App.js
|
||||
|
||||
```js
|
||||
// src/App.js
|
||||
import React from 'react
|
||||
const App = () => <h1>Welcome to 30 Days Of React</h1>
|
||||
```
|
||||
|
||||
We have to export the component to import it in another file. We can export it as a default or named export. In one file, we can make one default export and many named exports. First, let's implement it using name export and later in default export.
|
||||
|
||||
We just add the keyword export before _let_ or _const_ to make a named export.
|
||||
|
||||
```js
|
||||
// src/App.js
|
||||
import React from 'react
|
||||
|
||||
// named export in arrow function
|
||||
export const App = () => <h1>Welcome to 30 Days Of React</h1>
|
||||
```
|
||||
|
||||
Exporting in a function declaration, a regular function
|
||||
|
||||
```js
|
||||
// src/App.js
|
||||
import React from 'react
|
||||
// named export in regular function, function declaration
|
||||
export function App () {
|
||||
return <h1>Welcome to 30 Days Of React</h1>
|
||||
}
|
||||
```
|
||||
|
||||
Now, let's import the App component from the App.js file to index.js.
|
||||
|
||||
```js
|
||||
// index.js
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { App } from './App'
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
We saw a named export and now let's implement it with default export. We can do it in two ways but it is recommended to use the second way if we are exporting components because sometimes we may bind a component with another higher order component.
|
||||
|
||||
```js
|
||||
// src/App.js
|
||||
import React from 'react
|
||||
// export default in arrow function
|
||||
export default const App = () => <h1>Welcome to 30 Days Of React</h1>
|
||||
|
||||
```
|
||||
|
||||
```js
|
||||
// src/App.js
|
||||
import React from 'react
|
||||
// export default in arrow function
|
||||
export default function App () {
|
||||
return <h1>Welcome to 30 Days Of React</h1>
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
// src/App.js
|
||||
// Recommended for most of the cases
|
||||
import React from 'react
|
||||
const App = () => <h1>Welcome to 30 Days Of React</h1>
|
||||
export default App
|
||||
```
|
||||
|
||||
If a component is exported as default we do not need curly bracket during importing.
|
||||
|
||||
```js
|
||||
// index.js
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import App from './App'
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
If you remember, we created the following components so far and we have been putting them together. It is not easy to work like this. Now we will move them all components to a separate file.
|
||||
|
||||
```js
|
||||
// index.js
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import asabenehImage from './images'
|
||||
import { countriesData } from './data/countries'
|
||||
|
||||
// Header component
|
||||
class Header extends React.Component {
|
||||
render() {
|
||||
console.log(this.props.data)
|
||||
const {
|
||||
welcome,
|
||||
title,
|
||||
subtitle,
|
||||
author: { firstName, lastName },
|
||||
date,
|
||||
} = this.props.data
|
||||
|
||||
return (
|
||||
<header>
|
||||
<div className='header-wrapper'>
|
||||
<h1>{welcome}</h1>
|
||||
<h2>{title}</h2>
|
||||
<h3>{subtitle}</h3>
|
||||
<p>
|
||||
{firstName} {lastName}
|
||||
</p>
|
||||
<small>{date}</small>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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`
|
||||
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.join(', ')}
|
||||
</p>
|
||||
<p>
|
||||
<span>Population: </span>
|
||||
{population.toLocaleString()}
|
||||
</p>
|
||||
<p>
|
||||
<span>Currency: </span>
|
||||
{currency}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// User Card Component
|
||||
const UserCard = () => (
|
||||
<div className='user-card'>
|
||||
<img src={asabenehImage} alt='asabeneh image' />
|
||||
<h2>Asabeneh Yetayeh</h2>
|
||||
</div>
|
||||
)
|
||||
|
||||
// Hexadecimal color generator
|
||||
const hexaColor = () => {
|
||||
let str = '0123456789abcdef'
|
||||
let color = ''
|
||||
for (let i = 0; i < 6; i++) {
|
||||
let index = Math.floor(Math.random() * str.length)
|
||||
color += str[index]
|
||||
}
|
||||
return '#' + color
|
||||
}
|
||||
|
||||
const HexaColor = () => <div>{hexaColor()}</div>
|
||||
|
||||
const Message = ({ message }) => (
|
||||
<div>
|
||||
<h1>{message}</h1>
|
||||
</div>
|
||||
)
|
||||
const Login = () => (
|
||||
<div>
|
||||
<h3>Please Login</h3>
|
||||
</div>
|
||||
)
|
||||
const Welcome = (props) => (
|
||||
<div>
|
||||
<h1>Welcome to 30 Days Of React</h1>
|
||||
</div>
|
||||
)
|
||||
|
||||
// A button component
|
||||
const Button = ({ text, onClick, style }) => (
|
||||
<button style={style} onClick={onClick}>
|
||||
{text}
|
||||
</button>
|
||||
)
|
||||
|
||||
// TechList Component
|
||||
// class base component
|
||||
class TechList extends React.Component {
|
||||
render() {
|
||||
const { techs } = this.props
|
||||
const techsFormatted = techs.map((tech) => <li key={tech}>{tech}</li>)
|
||||
return techsFormatted
|
||||
}
|
||||
}
|
||||
|
||||
// Main Component
|
||||
// Class Component
|
||||
class Main extends React.Component {
|
||||
render() {
|
||||
const {
|
||||
techs,
|
||||
greetPeople,
|
||||
handleTime,
|
||||
loggedIn,
|
||||
handleLogin,
|
||||
message,
|
||||
} = this.props
|
||||
console.log(message)
|
||||
|
||||
const status = loggedIn ? <Welcome /> : <Login />
|
||||
return (
|
||||
<main>
|
||||
<div className='main-wrapper'>
|
||||
<p>Prerequisite to get started react.js:</p>
|
||||
<ul>
|
||||
<TechList techs={this.props.techs} />
|
||||
</ul>
|
||||
{techs.length === 3 && (
|
||||
<p>You have all the prerequisite courses to get started React</p>
|
||||
)}
|
||||
<div>
|
||||
<Button
|
||||
text='Show Time'
|
||||
onClick={handleTime}
|
||||
style={buttonStyles}
|
||||
/>{' '}
|
||||
<Button
|
||||
text='Greet People'
|
||||
onClick={greetPeople}
|
||||
style={buttonStyles}
|
||||
/>
|
||||
{!loggedIn && <p>Please login to access more information about 30 Days Of React challenge</p>}
|
||||
</div>
|
||||
<div style={{ margin: 30 }}>
|
||||
<Button
|
||||
text={loggedIn ? 'Logout' : 'Login'}
|
||||
style={buttonStyles}
|
||||
onClick={handleLogin}
|
||||
/>
|
||||
<br />
|
||||
{status}
|
||||
</div>
|
||||
<Message message={message} />
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// CSS styles in JavaScript Object
|
||||
const buttonStyles = {
|
||||
backgroundColor: '#61dbfb',
|
||||
padding: 10,
|
||||
border: 'none',
|
||||
borderRadius: 5,
|
||||
margin: 3,
|
||||
cursor: 'pointer',
|
||||
fontSize: 22,
|
||||
color: 'white',
|
||||
margin: '0 auto',
|
||||
}
|
||||
|
||||
// Footer Component
|
||||
// Class component
|
||||
class Footer extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<footer>
|
||||
<div className='footer-wrapper'>
|
||||
<p>Copyright {this.props.date.getFullYear()}</p>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class App extends React.Component {
|
||||
state = {
|
||||
loggedIn: false,
|
||||
techs: ['HTML', 'CSS', 'JS'],
|
||||
message: 'Click show time or Greet people to change me',
|
||||
}
|
||||
handleLogin = () => {
|
||||
this.setState({
|
||||
loggedIn: !this.state.loggedIn,
|
||||
})
|
||||
}
|
||||
showDate = (time) => {
|
||||
const months = [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December',
|
||||
]
|
||||
|
||||
const month = months[time.getMonth()].slice(0, 3)
|
||||
const year = time.getFullYear()
|
||||
const date = time.getDate()
|
||||
return `${month} ${date}, ${year}`
|
||||
}
|
||||
handleTime = () => {
|
||||
let message = this.showDate(new Date())
|
||||
this.setState({ message })
|
||||
}
|
||||
greetPeople = () => {
|
||||
let message = 'Welcome to 30 Days Of React Challenge, 2020'
|
||||
this.setState({ message })
|
||||
}
|
||||
|
||||
render() {
|
||||
const data = {
|
||||
welcome: '30 Days Of React',
|
||||
title: 'Getting Started React',
|
||||
subtitle: 'JavaScript Library',
|
||||
author: {
|
||||
firstName: 'Asabeneh',
|
||||
lastName: 'Yetayeh',
|
||||
},
|
||||
date: 'Oct 9, 2020',
|
||||
}
|
||||
const techs = ['HTML', 'CSS', 'JavaScript']
|
||||
|
||||
return (
|
||||
<div className='app'>
|
||||
{this.state.backgroundColor}
|
||||
<Header data={data} />
|
||||
|
||||
<Main
|
||||
techs={techs}
|
||||
handleTime={this.handleTime}
|
||||
greetPeople={this.greetPeople}
|
||||
loggedIn={this.state.loggedIn}
|
||||
handleLogin={this.handleLogin}
|
||||
message={this.state.message}
|
||||
/>
|
||||
|
||||
<Footer date={new Date()} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
## Components Folder
|
||||
|
||||
Inside the src directory will pull all the components
|
||||
|
||||
```sh
|
||||
src
|
||||
App.js
|
||||
index.js
|
||||
components
|
||||
-auth
|
||||
-Signup.js
|
||||
-Signin.js
|
||||
-Forgotpassword.js
|
||||
-Resetpassord.js
|
||||
header
|
||||
-Header.js
|
||||
footer
|
||||
-Footer.js
|
||||
assets
|
||||
-images
|
||||
-icnons
|
||||
- fonts
|
||||
styles
|
||||
-button.js
|
||||
-button.scss
|
||||
utils
|
||||
-random-id.js
|
||||
-display-time.js
|
||||
-generate-color.js
|
||||
shared
|
||||
-Button.js
|
||||
-InputField.js
|
||||
-TextAreaField.js
|
||||
```
|
||||
|
||||
Let's create components directory inside src and inside components let's create header director. Create Header.js inside the header directory.
|
||||
|
||||
```js
|
||||
// src/components/header/Header.js
|
||||
import React from 'react'
|
||||
|
||||
const Header = (props) => {
|
||||
return (
|
||||
<header>
|
||||
<div className='header-wrapper'>
|
||||
<h1>{welcome}</h1>
|
||||
<h2>{title}</h2>
|
||||
<h3>{subtitle}</h3>
|
||||
<p>
|
||||
{firstName} {lastName}
|
||||
</p>
|
||||
<small>{date}</small>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
export default Header
|
||||
```
|
||||
|
||||
Similar to the Header let's move all the components to their respective files.
|
||||
All the CSS files on index.html will moved into styles folder and after that each part has been split its respective file, try to check the styles folder.
|
||||
|
||||
## Fragments
|
||||
|
||||
Fragments are a way to avoid unnecessary parent element in JSX. Let's implement a fragment. We import fragment from react module. As you can see below, we imported React and fragment together by use a comma separation.
|
||||
|
||||
```js
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
const Skills = () => {
|
||||
return (
|
||||
<Fragment>
|
||||
<li>HTML</li>
|
||||
<li>CSS</li>
|
||||
<li>JavaScript</li>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
const RequiredSkills = () => {
|
||||
return (
|
||||
<ul>
|
||||
<Skills />
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
It is also possible to just extract the fragment module from React as shown below.
|
||||
|
||||
```js
|
||||
import React from 'react'
|
||||
|
||||
const Skills = () => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<li>HTML</li>
|
||||
<li>CSS</li>
|
||||
<li>JavaScript</li>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
const RequiredSkills = () => {
|
||||
return (
|
||||
<ul>
|
||||
<Skills />
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
In latest version of Reacts it also possible to write without extracting or importing using this signs(<> </>)
|
||||
|
||||
```js
|
||||
import React from 'react'
|
||||
|
||||
// Recommended
|
||||
const Skills = () => {
|
||||
return (
|
||||
<>
|
||||
<li>HTML</li>
|
||||
<li>CSS</li>
|
||||
<li>JavaScript</li>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const RequiredSkills = () => {
|
||||
return (
|
||||
<ul>
|
||||
<Skills />
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
When we make a class-based component we have been using React.Component instead we can just import the component and the code will look more clean. Let's see an example.
|
||||
|
||||
```js
|
||||
import React from 'react'
|
||||
|
||||
// without importing the Component
|
||||
// Not recommended
|
||||
class App extends React.Component {
|
||||
render() {
|
||||
return <h1> 30 Days of React </h1>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
import React, { Component } from 'react'
|
||||
|
||||
// This is recommended
|
||||
class App extends Component {
|
||||
render() {
|
||||
return <h1> 30 Days of React </h1>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Well done. Time to do some exercises for your brain and muscles.
|
||||
|
||||
# Exercises
|
||||
|
||||
## Exercises:Level 1
|
||||
|
||||
1. What is the importance of React Folder Structure and File Naming
|
||||
2. How do we export file
|
||||
3. How do we import file
|
||||
4. Make a component of module and export it as named or default export
|
||||
5. Make a component or module and import it
|
||||
6. Change all the components you have to different folder structure
|
||||
|
||||
## Exercises:Level 2
|
||||
|
||||
1. Make a simple a simple portfolio using the components we have created so far.
|
||||
|
||||
🎉 CONGRATULATIONS ! 🎉
|
||||
|
||||
[<< Day 9](../09_Day_Conditional_Rendering/09_conditional_rendering.md) | [Day 11 >>](../11_Day_Events/10_events.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 10
|
||||
|
||||
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,20 @@
|
||||
<!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|Aldrich:300,400, 500|Oswald:300,400, 500|Raleway: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>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,65 @@
|
||||
import React from 'react'
|
||||
import Header from './components/header/Header'
|
||||
import Main from './components/main/Main'
|
||||
import Footer from './components/footer/Footer'
|
||||
import { countriesData } from './data/countries'
|
||||
import asabenehImage from './assets/images/asabeneh.jpg'
|
||||
import { showDate } from './utils/display-date-and-time'
|
||||
|
||||
class App extends React.Component {
|
||||
state = {
|
||||
loggedIn: false,
|
||||
techs: ['HTML', 'CSS', 'JS'],
|
||||
message: 'Click show time or Greet people to change me',
|
||||
country: countriesData[1],
|
||||
}
|
||||
handleLogin = () => {
|
||||
this.setState({
|
||||
loggedIn: !this.state.loggedIn,
|
||||
})
|
||||
}
|
||||
handleTime = () => {
|
||||
let message = showDate(new Date())
|
||||
this.setState({ message })
|
||||
}
|
||||
greetPeople = () => {
|
||||
let message = 'Welcome to 30 Days Of React Challenge, 2020'
|
||||
this.setState({ message })
|
||||
}
|
||||
|
||||
render() {
|
||||
const data = {
|
||||
welcome: '30 Days Of React',
|
||||
title: 'Getting Started React',
|
||||
subtitle: 'JavaScript Library',
|
||||
author: {
|
||||
firstName: 'Asabeneh',
|
||||
lastName: 'Yetayeh',
|
||||
},
|
||||
date: new Date(),
|
||||
}
|
||||
const techs = ['HTML', 'CSS', 'JavaScript']
|
||||
const user = { ...data.author, image: asabenehImage }
|
||||
|
||||
return (
|
||||
<div className='app'>
|
||||
{this.state.backgroundColor}
|
||||
<Header data={data} />
|
||||
<Main
|
||||
techs={techs}
|
||||
handleTime={this.handleTime}
|
||||
greetPeople={this.greetPeople}
|
||||
loggedIn={this.state.loggedIn}
|
||||
handleLogin={this.handleLogin}
|
||||
message={this.state.message}
|
||||
country={this.state.country}
|
||||
user={user}
|
||||
/>
|
||||
|
||||
<Footer date={new Date()} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default App
|
@ -0,0 +1,13 @@
|
||||
import React from 'react'
|
||||
import { hexaColor } from '../../utils/hexadecimal-color-generator'
|
||||
const HexaColor = (props) => {
|
||||
return (
|
||||
<div style={{ textAlign: 'center', border: '2px solid', height: 50 }}>
|
||||
{hexaColor()}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
HexaColor.propTypes = {}
|
||||
|
||||
export default HexaColor
|
@ -0,0 +1,40 @@
|
||||
import React from 'react'
|
||||
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`
|
||||
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.join(', ')}
|
||||
</p>
|
||||
<p>
|
||||
<span>Population: </span>
|
||||
{population.toLocaleString()}
|
||||
</p>
|
||||
<p>
|
||||
<span>Currency: </span>
|
||||
{currency}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Country
|
@ -0,0 +1,11 @@
|
||||
import React from 'react'
|
||||
const Footer = ({ date }) => {
|
||||
return (
|
||||
<footer>
|
||||
<div className='footer-wrapper'>
|
||||
<p>Copyright {date.getFullYear()}</p>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
export default Footer
|
@ -0,0 +1,29 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { showDate } from '../../utils/display-date-and-time'
|
||||
|
||||
const Header = ({
|
||||
data: {
|
||||
welcome,
|
||||
title,
|
||||
subtitle,
|
||||
author: { firstName, lastName },
|
||||
date,
|
||||
},
|
||||
}) => {
|
||||
return (
|
||||
<header>
|
||||
<div className='header-wrapper'>
|
||||
<h1>{welcome}</h1>
|
||||
<h2>{title}</h2>
|
||||
<h3>{subtitle}</h3>
|
||||
<p>
|
||||
{firstName} {lastName}
|
||||
</p>
|
||||
<small>{showDate(date)}</small>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
export default Header
|
@ -0,0 +1,108 @@
|
||||
import React from 'react'
|
||||
import Button from '../shared/Button'
|
||||
import HexaColr from '../color/HexaColor'
|
||||
import Country from '../country/Country'
|
||||
import UserCard from '../user/UserCard'
|
||||
import { buttonStyles } from '../../styles/button-styles'
|
||||
|
||||
|
||||
// TechList Component
|
||||
// class base component
|
||||
class TechList extends React.Component {
|
||||
render() {
|
||||
const { techs } = this.props
|
||||
const techsFormatted = techs.map((tech) => <li key={tech}>{tech}</li>)
|
||||
return techsFormatted
|
||||
}
|
||||
}
|
||||
|
||||
const Message = ({ message }) => (
|
||||
<div
|
||||
style={{
|
||||
border: '2px solid #61dbfb',
|
||||
margin: 25,
|
||||
padding: 10,
|
||||
}}
|
||||
>
|
||||
<h3>{message}</h3>
|
||||
</div>
|
||||
)
|
||||
const Login = () => (
|
||||
<div>
|
||||
<h3>Please Login</h3>
|
||||
</div>
|
||||
)
|
||||
const Welcome = (props) => (
|
||||
<div style={{ border: '2px solid rgb(0,255,0', margin: 10, padding: 10 }}>
|
||||
<h2>Welcome to 30 Days Of React</h2>
|
||||
</div>
|
||||
)
|
||||
|
||||
// Main Component
|
||||
// Class Component
|
||||
class Main extends React.Component {
|
||||
render() {
|
||||
const {
|
||||
techs,
|
||||
greetPeople,
|
||||
handleTime,
|
||||
loggedIn,
|
||||
handleLogin,
|
||||
message,
|
||||
country,
|
||||
user,
|
||||
} = this.props
|
||||
console.log(message)
|
||||
|
||||
const status = loggedIn ? <Welcome /> : <Login />
|
||||
return (
|
||||
<main>
|
||||
<div className='main-wrapper'>
|
||||
<p>Prerequisite to get started react.js:</p>
|
||||
<ul>
|
||||
<TechList techs={this.props.techs} />
|
||||
</ul>
|
||||
|
||||
<UserCard user={user} />
|
||||
|
||||
{techs.length === 3 && (
|
||||
<p>You have all the prerequisite courses to get started React</p>
|
||||
)}
|
||||
<div>
|
||||
<Button
|
||||
text='Show Time'
|
||||
onClick={handleTime}
|
||||
style={buttonStyles}
|
||||
/>{' '}
|
||||
<Button
|
||||
text='Greet People'
|
||||
onClick={greetPeople}
|
||||
style={buttonStyles}
|
||||
/>
|
||||
{!loggedIn && (
|
||||
<p>
|
||||
Please login to access more information about 30 Days Of React
|
||||
challenge
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ margin: 30 }}>
|
||||
<Button
|
||||
text={loggedIn ? 'Logout' : 'Login'}
|
||||
style={buttonStyles}
|
||||
onClick={handleLogin}
|
||||
/>
|
||||
<br />
|
||||
{status}
|
||||
</div>
|
||||
<Message message={message} />
|
||||
<HexaColr />
|
||||
<HexaColr />
|
||||
<Country country={country} />
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Main
|
@ -0,0 +1,11 @@
|
||||
import React from 'react'
|
||||
|
||||
// A button component
|
||||
const Button = ({ text, onClick, style }) => (
|
||||
<button style={style} onClick={onClick}>
|
||||
{text}
|
||||
</button>
|
||||
)
|
||||
|
||||
export default Button
|
||||
|
@ -0,0 +1,15 @@
|
||||
import React from 'react'
|
||||
|
||||
// User Card Component
|
||||
const UserCard = ({ user: { firstName, lastName, image } }) => (
|
||||
<div className='user-card'>
|
||||
<img src={image} alt='asabeneh image' />
|
||||
|
||||
<h2>
|
||||
{firstName}
|
||||
{lastName}
|
||||
</h2>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default UserCard
|
@ -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 },
|
||||
]
|
@ -0,0 +1,10 @@
|
||||
// index.js
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import App from './App'
|
||||
import './styles/index.css'
|
||||
|
||||
// class based component
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
@ -0,0 +1,12 @@
|
||||
// CSS styles in JavaScript Object
|
||||
export const buttonStyles = {
|
||||
backgroundColor: '#61dbfb',
|
||||
padding: 10,
|
||||
border: 'none',
|
||||
borderRadius: 5,
|
||||
margin: 3,
|
||||
cursor: 'pointer',
|
||||
fontSize: 22,
|
||||
color: 'white',
|
||||
margin: '0 auto',
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
line-height: 1.5;
|
||||
font-family: 'Roboto';
|
||||
font-weight: 300;
|
||||
color: black;
|
||||
overflow-x: hidden;
|
||||
font-size: 110%;
|
||||
}
|
||||
#root {
|
||||
min-height: 100%;
|
||||
position: relative;
|
||||
letter-spacing: 1.25px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 10px;
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
ul li {
|
||||
list-style: none;
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/* Countries*/
|
||||
.country {
|
||||
max-width: 50rem;
|
||||
min-width: 50rem;
|
||||
height: 35rem;
|
||||
text-align: center;
|
||||
margin: 0.75rem auto;
|
||||
padding: 2rem;
|
||||
border-radius: 0.2rem;
|
||||
background: white;
|
||||
box-shadow: 0 0.1rem 1rem #cfc9c7;
|
||||
}
|
||||
|
||||
.country:hover {
|
||||
box-shadow: 0 0.1rem 1rem #cfc9c7;
|
||||
-webkit-transition: all 0.2s ease-in;
|
||||
transform: scale(1.015);
|
||||
}
|
||||
|
||||
.country_flag {
|
||||
height: 12rem;
|
||||
width: 20rem;
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.country img {
|
||||
display: block;
|
||||
margin: auto;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
border-radius: 0.3rem;
|
||||
box-shadow: 0 0 0.6rem 0.2rem rgb(241, 225, 225);
|
||||
}
|
||||
|
||||
.country_name {
|
||||
font-size: 1.6rem;
|
||||
color: #ffa500;
|
||||
letter-spacing: 0.075rem;
|
||||
font-weight: bolder;
|
||||
margin: 1rem;
|
||||
color: #414141;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.country p {
|
||||
font-size: 1.6rem;
|
||||
font-weight: 500;
|
||||
padding: 0.2rem;
|
||||
color: #747474;
|
||||
|
||||
text-align: left;
|
||||
letter-spacing: 0.05rem;
|
||||
}
|
||||
|
||||
.country span {
|
||||
font-weight: 600;
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
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;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
header {
|
||||
background-color: #61dbfb;
|
||||
padding: 25;
|
||||
padding: 10px;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
/* == General style === */
|
||||
/* This CSS has to be broken into small files */
|
||||
|
||||
@import './common.css';
|
||||
@import './header.css';
|
||||
@import './footer.css';
|
||||
@import './user-card.css';
|
||||
@import './country.css';
|
@ -0,0 +1,7 @@
|
||||
.user-card {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.user-card > img {
|
||||
border-radius: 50%;
|
||||
width: 14%;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
export const showDate = (time) => {
|
||||
const months = [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December',
|
||||
]
|
||||
|
||||
const month = months[time.getMonth()].slice(0, 3)
|
||||
const year = time.getFullYear()
|
||||
const date = time.getDate()
|
||||
return `${month} ${date}, ${year}`
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
// Hexadecimal color generator
|
||||
export const hexaColor = () => {
|
||||
let str = '0123456789abcdef'
|
||||
let color = ''
|
||||
for (let i = 0; i < 6; i++) {
|
||||
let index = Math.floor(Math.random() * str.length)
|
||||
color += str[index]
|
||||
}
|
||||
return '#' + color
|
||||
}
|
@ -0,0 +1,230 @@
|
||||
<div align="center">
|
||||
<h1> 30 Days Of React: Statet</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 10](../10_React_Project_Folder_Structure/10_react_project_folder_structure.md) | [Day 12 >>](../12_Day_Forms/12_forms.md)
|
||||
|
||||

|
||||
|
||||
- [Events](#events)
|
||||
- [What is an event?](#what-is-an-event)
|
||||
- [Exercises](#exercises)
|
||||
- [Exercises: Level 1](#exercises-level-1)
|
||||
- [Exercises: Level 2](#exercises-level-2)
|
||||
- [Exercises: Level 3](#exercises-level-3)
|
||||
|
||||
# Events
|
||||
|
||||
## What is an event?
|
||||
|
||||
An event is an action or occurrence recognized by a software. To make an event more clear let's use the daily activities we do when we use a computer such as clicking on a button, hover on an image, pressing a keyboard, scrolling the mouse wheel and etc. In this section, we will focus only some of the mouse and keyboard events. The react documentation has already a detail note about [events](https://reactjs.org/docs/handling-events.html).
|
||||
|
||||
Handling events in React is very similar to handling elements on DOM elements using pure JavaScript. Some of the syntax difference between handling event in React and pure JavaScript:
|
||||
|
||||
- React events are named using camelCase, rather than lowercase.
|
||||
- With JSX you pass a function as the event handler, rather than a string.
|
||||
|
||||
Let's see some examples to understand event handling.
|
||||
|
||||
Event handling in HTML
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>30 Days Of React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<button>onclick="greetPeople()">Greet People</button>
|
||||
<script>
|
||||
const greetPeople = () => {
|
||||
alert('Welcome to 30 Days Of React Challenge')
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
In React, it is slightly different
|
||||
|
||||
```js
|
||||
import React from 'react'
|
||||
// if it is functional components
|
||||
const App = () => {
|
||||
const greetPeople = () => {
|
||||
alert('Welcome to 30 Days Of React Challenge')
|
||||
}
|
||||
return <button onClick={greetPeople}> </button>
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
import React, { Component } from 'react'
|
||||
// if it is functional components
|
||||
class App extends Component {
|
||||
greetPeople = () => {
|
||||
alert('Welcome to 30 Days Of React Challenge')
|
||||
}
|
||||
render() {
|
||||
return <button onClick={this.greetPeople}> </button>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Another difference between HTML and React event is that you cannot return false to prevent default behavior in React. You must call preventDefault explicitly. For example, with plain HTML, to prevent the default link behavior of opening a new page, you can write:
|
||||
|
||||
Plain HTML
|
||||
|
||||
```html
|
||||
<a href="#" onclick="console.log('The link was clicked.'); return false">
|
||||
Click me
|
||||
</a>
|
||||
```
|
||||
|
||||
However, in React it could be as follows:
|
||||
|
||||
```js
|
||||
import React, { Component } from 'react'
|
||||
// if it is functional components
|
||||
class App extends Component {
|
||||
handleClick = () => {
|
||||
alert('Welcome to 30 Days Of React Challenge')
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<a href='#' onClick={this.handleClick}>
|
||||
Click me
|
||||
</a>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Event handling is a very vast topic and in this challenge we will focus on the most common event types. We may use the following mouse and keyboard events.
|
||||
_onMouseMove, onMouseEnter, onMouseLeave, onMouseOut, onClick, onKeyDown, onKeyPress, onKeyUp, onCopy, onCut, onDrag, onChange,onBlur,onInput, onSubmit_
|
||||
|
||||
Let's implement some more mouse and keyboard events.
|
||||
|
||||
```js
|
||||
// index.js
|
||||
import React, { Component } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
/*
|
||||
_onMouseMove, onMouseEnter, onMouseLeave, onMouseOut, onClick, onKeyDown, onKeyPress, onKeyUp, onCopy, onCut, onDrag, onChange,onBlur,onInput, onSubmit_
|
||||
|
||||
*/
|
||||
|
||||
class App extends Component {
|
||||
state = {
|
||||
firstName: '',
|
||||
message: '',
|
||||
key: '',
|
||||
}
|
||||
handleClick = (e) => {
|
||||
// e gives an event object
|
||||
// check the value of e using console.log(e)
|
||||
this.setState({
|
||||
message: 'Welcome to the world of events',
|
||||
})
|
||||
}
|
||||
// triggered whenever the mouse moves
|
||||
handleMouseMove = (e) => {
|
||||
this.setState({ message: 'mouse is moving' })
|
||||
}
|
||||
// to get value when an input field changes a value
|
||||
handleChange = (e) => {
|
||||
this.setState({
|
||||
firstName: e.target.value,
|
||||
message: e.target.value,
|
||||
})
|
||||
}
|
||||
|
||||
// to get keyboard key code when an input field is pressed
|
||||
// it works with input and textarea
|
||||
handleKeyPress = (e) => {
|
||||
this.setState({
|
||||
message:
|
||||
`${e.target.value} has been pressed and the keycode is` + e.charCode,
|
||||
})
|
||||
}
|
||||
// Blurring happens when a mouse leave an input field
|
||||
handleBlur = (e) => {
|
||||
this.setState({ message: 'Input field has been blurred' })
|
||||
}
|
||||
// This event triggers during a text copy
|
||||
handleCopy = (e) => {
|
||||
this.setState({
|
||||
message: 'Using 30 Days Of React for commercial purpose is not allowed',
|
||||
})
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Welcome to the World of Events</h1>
|
||||
|
||||
<button onClick={this.handleClick}>Click Me</button>
|
||||
<button onMouseMove={this.handleMouseMove}>Move mouse on me</button>
|
||||
<p onCopy={this.handleCopy}>
|
||||
Check copy right permission by copying this text
|
||||
</p>
|
||||
|
||||
<p>{this.state.message}</p>
|
||||
<label htmlFor=''> Test for onKeyPress Event: </label>
|
||||
<input type='text' onKeyPress={this.handleKeyPress} />
|
||||
<br />
|
||||
|
||||
<label htmlFor=''> Test for onBlur Event: </label>
|
||||
<input type='text' onBlur={this.handleBlur} />
|
||||
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<div>
|
||||
<label htmlFor='firstName'>First Name: </label>
|
||||
<input
|
||||
onChange={this.handleChange}
|
||||
name='firstName'
|
||||
value={this.state.value}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type='submit' value='Submit' />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
// we render the JSX element using the ReactDOM package
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
# Exercises
|
||||
|
||||
## Exercises: Level 1
|
||||
|
||||
## Exercises: Level 2
|
||||
|
||||
Implement the following using onMouseEnter event
|
||||
|
||||

|
||||
|
||||
## Exercises: Level 3
|
||||
|
||||
🎉 CONGRATULATIONS ! 🎉
|
||||
|
||||
[<< Day 10](../10_React_Project_Folder_Structure/10_react_project_folder_structure.md) | [Day 12 >>](../12_Day_Forms/12_forms.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 3
|
||||
|
||||
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,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<title>30 Days Of React App</title>
|
||||
</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,87 @@
|
||||
// index.js
|
||||
import React, { Component } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
class App extends Component {
|
||||
state = {
|
||||
firstName: '',
|
||||
message: '',
|
||||
key: '',
|
||||
}
|
||||
handleClick = (e) => {
|
||||
// e gives an event object
|
||||
// check the value of e using console.log(e)
|
||||
this.setState({
|
||||
message: 'Welcome to the world of events',
|
||||
})
|
||||
}
|
||||
// triggered whenever the mouse moves
|
||||
handleMouseMove = (e) => {
|
||||
this.setState({ message: 'mouse is moving' })
|
||||
}
|
||||
// to get value when an input field changes a value
|
||||
handleChange = (e) => {
|
||||
this.setState({
|
||||
firstName: e.target.value,
|
||||
message: e.target.value,
|
||||
})
|
||||
}
|
||||
|
||||
// to get keyboard key code when an input field is pressed
|
||||
// it works with input and textarea
|
||||
handleKeyPress = (e) => {
|
||||
this.setState({
|
||||
message:
|
||||
`${e.target.value} has been pressed and the keycode is` + e.charCode,
|
||||
})
|
||||
}
|
||||
// Blurring happens when a mouse leave an input field
|
||||
handleBlur = (e) => {
|
||||
this.setState({ message: 'Input field has been blurred' })
|
||||
}
|
||||
// This event triggers during a text copy
|
||||
handleCopy = (e) => {
|
||||
this.setState({
|
||||
message: 'Using 30 Days Of React for commercial purpose is not allowed',
|
||||
})
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Welcome to the World of Events</h1>
|
||||
|
||||
<button onClick={this.handleClick}>Click Me</button>
|
||||
<button onMouseMove={this.handleMouseMove}>Move mouse on me</button>
|
||||
<p onCopy={this.handleCopy}>
|
||||
Check copy right permission by copying this text
|
||||
</p>
|
||||
|
||||
<p>{this.state.message}</p>
|
||||
<label htmlFor=''> Test for onKeyPress Event: </label>
|
||||
<input type='text' onKeyPress={this.handleKeyPress} />
|
||||
<br />
|
||||
|
||||
<label htmlFor=''> Test for onBlur Event: </label>
|
||||
<input type='text' onBlur={this.handleBlur} />
|
||||
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<div>
|
||||
<label htmlFor='firstName'>First Name: </label>
|
||||
<input
|
||||
onChange={this.handleChange}
|
||||
name='firstName'
|
||||
value={this.state.value}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type='submit' value='Submit' />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
@ -0,0 +1,874 @@
|
||||
<div align="center">
|
||||
<h1> 30 Days Of React: Forms</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 11](../11_Day_Events/11_events.md) | [Day 13 >>]()
|
||||
|
||||

|
||||
|
||||
- [Forms](#forms)
|
||||
- [Getting data from input field](#getting-data-from-input-field)
|
||||
- [Getting multiple input data from form](#getting-multiple-input-data-from-form)
|
||||
- [Get data from different input field types](#get-data-from-different-input-field-types)
|
||||
- [Form Validation](#form-validation)
|
||||
- [What is validation?](#what-is-validation)
|
||||
- [What is the purpose of validation](#what-is-the-purpose-of-validation)
|
||||
- [Validation Types](#validation-types)
|
||||
- [Exercises](#exercises)
|
||||
- [Exercises: Level 1](#exercises-level-1)
|
||||
- [Exercises: Level 2](#exercises-level-2)
|
||||
- [Exercises: Level 3](#exercises-level-3)
|
||||
|
||||
# Forms
|
||||
|
||||
Form is used to collect data from user. Once in a while we use form to fill our information on a paper or on a website. Either to sign up, sign in or to apply for a job we fill different form fields to submit data to remote database. We encounter different form fields when we fill a form such as simple text, email, password, telephone, date, checkbox, radio button, option selection and text area field. Currently, HTML5 has provide quite a lot of field types. You may have
|
||||
|
||||
```html
|
||||
<input type="text" />
|
||||
<input type="number" />
|
||||
<input type="range" />
|
||||
|
||||
<input type="email" />
|
||||
<input type="password" />
|
||||
<input type="tel" />
|
||||
|
||||
<input type="checkbox" />
|
||||
<input type="radio" />
|
||||
|
||||
<input type="color" />
|
||||
|
||||
<input type="url" />
|
||||
<input type="image" />
|
||||
<input type="file" />
|
||||
|
||||
<input type="hidden" />
|
||||
|
||||
<input type="date" />
|
||||
<input type="datetime-local" />
|
||||
<input type="month" />
|
||||
<input type="week" />
|
||||
<input type="time" />
|
||||
|
||||
<input type="reset" />
|
||||
<input type="search" />
|
||||
<input type="submit" />
|
||||
<input type="button" />
|
||||
```
|
||||
|
||||
Another HTML fields to get data from form are textarea and options.
|
||||
|
||||
```html
|
||||
<textarea>Please write your comment ...</textarea>
|
||||
|
||||
<select name="country">
|
||||
<option value="">Select your country</option>
|
||||
<option value="finland">Finland</option>
|
||||
<option value="sweden">Sweden</option>
|
||||
<option value="denmark">Denmark</option>
|
||||
<option value="norway">Norway</option>
|
||||
<option value="iceland">Iceland</option>
|
||||
</select>
|
||||
```
|
||||
|
||||
Now, you know most of fields we need to get data from a form. Let's start with an input of type text field. In the previous day, we saw different types of events and today we will focus on more of _onChange_ event type which triggers whenever an input field data changes. Input field has by default a memory to store input data but in this section we control that using state and we implement a controlled input. Today we will implement a controlled input. We will cover uncontrolled input in a separate section.
|
||||
|
||||
## Getting data from input field
|
||||
|
||||
So far we did not get any data from input field. Now, it is time to learn how to get data from an input field. We need on input field, event listener (onChange) and state to get data from a controlled input. See the example below. The h1 element below the input tag display what we write on the input. Check live [demo](https://codepen.io/Asabeneh/full/OJVpyqm).
|
||||
|
||||
The input element has many attributes such as value, name, id, placeholder, type and event handler. In addition, we can link a label and an input field using an id of input field and htmlFor of the label.If label and input are linked it will focus the input when we click on label. Look at the example give below.
|
||||
|
||||
```js
|
||||
class App extends React.Component {
|
||||
// declaring state
|
||||
// initial state
|
||||
state = {
|
||||
firstName: '',
|
||||
}
|
||||
handleChange = (e) => {
|
||||
const value = e.target.value
|
||||
this.setState({ firstName: value })
|
||||
}
|
||||
|
||||
render() {
|
||||
// accessing the state value and this value will injected to the input in the value attribute
|
||||
const firstName = this.state.firstName
|
||||
return (
|
||||
<div className='App'>
|
||||
<label htmlFor='firstName'>First Name: </l>
|
||||
<input
|
||||
type='text'
|
||||
id='firstName'
|
||||
name='firstName'
|
||||
placeholder='First Name'
|
||||
value={firstName}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<h1>{this.state.firstName}</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We usually use form to handle use information. Let us move to form section and make use the form element.
|
||||
|
||||
## Getting multiple input data from form
|
||||
|
||||
In this section we will develop a small form which collect user information. Our user is a student. We use a parent form element and certain number of input elements to collect user information. In addition to that we will have event listener for the form (onSubmit) and for the inputs (onChange). See the following example try to see the commonts too. You can also check the live [demo](https://codepen.io/Asabeneh/full/eYNvJda).
|
||||
|
||||
```js
|
||||
class App extends React.Component {
|
||||
// declaring state
|
||||
state = {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
country: '',
|
||||
title: '',
|
||||
}
|
||||
handleChange = (e) => {
|
||||
/*
|
||||
// we can get the name and value like this but we can also destructure it from e.target
|
||||
const name = e.target.name
|
||||
const value = e.target.value
|
||||
*/
|
||||
const { name, value } = e.target
|
||||
// [variablename] this 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
|
||||
this.setState({ [name]: value })
|
||||
}
|
||||
handleSubmit = (e) => {
|
||||
// stops the default behavior of form element specifically refreshing of page
|
||||
e.preventDefault()
|
||||
console.log(this.state)
|
||||
}
|
||||
|
||||
render() {
|
||||
// accessing the state value by destrutcturing the state
|
||||
const { firstName, lastName, title, country } = this.state
|
||||
return (
|
||||
<div className='App'>
|
||||
<h3>Add Student</h3>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<div>
|
||||
<input
|
||||
type='text'
|
||||
name='firstName'
|
||||
placeholder='First Name'
|
||||
value={firstName}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type='text'
|
||||
name='lastName'
|
||||
placeholder='Last Name'
|
||||
value={lastName}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type='text'
|
||||
name='country'
|
||||
placeholder='Country'
|
||||
value={country}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type='text'
|
||||
name='title'
|
||||
placeholder='Title'
|
||||
value={title}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button onClick={this.changeAnimal} class='btn btn-success'>
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The above form handles only text types but can handle different input field types. Let's do another form which handle all the different input field types.
|
||||
|
||||
## Get data from different input field types
|
||||
|
||||
```js
|
||||
// index.js
|
||||
import React, { Component } 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 value={value}> {label}</option>
|
||||
))
|
||||
|
||||
class App extends React.Component {
|
||||
// declaring state
|
||||
state = {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
email: '',
|
||||
country: '',
|
||||
tel: '',
|
||||
dateOfBirth: '',
|
||||
favoriteColor: '',
|
||||
weight: '',
|
||||
gender: '',
|
||||
file: '',
|
||||
bio: '',
|
||||
skills: {
|
||||
html: false,
|
||||
css: false,
|
||||
javascript: false,
|
||||
},
|
||||
}
|
||||
handleChange = (e) => {
|
||||
/*
|
||||
// we can get the name and value like this but we can also destructure it from e.target
|
||||
const name = e.target.name
|
||||
const value = e.target.value
|
||||
*/
|
||||
const { name, value, type, checked } = e.target
|
||||
// [variablename] this 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') {
|
||||
this.setState({
|
||||
skills: { ...this.state.skills, [name]: checked },
|
||||
})
|
||||
} else if (type === 'file') {
|
||||
console.log(type, 'cehck here')
|
||||
this.setState({ [name]: e.target.files[0] })
|
||||
} else {
|
||||
this.setState({ [name]: value })
|
||||
}
|
||||
}
|
||||
handleSubmit = (e) => {
|
||||
// stops the default behavior of form element specifically refreshing of page
|
||||
e.preventDefault()
|
||||
e.preventDefault()
|
||||
const {
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
country,
|
||||
gender,
|
||||
bio,
|
||||
file,
|
||||
skills,
|
||||
} = this.state
|
||||
|
||||
const formattedSkills = []
|
||||
for (const key in skills) {
|
||||
console.log(key)
|
||||
if (skills[key]) {
|
||||
formattedSkills.push(key.toUpperCase())
|
||||
}
|
||||
}
|
||||
const data = {
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
country,
|
||||
gender,
|
||||
bio,
|
||||
file,
|
||||
skills: formattedSkills,
|
||||
}
|
||||
console.log(data)
|
||||
}
|
||||
|
||||
render() {
|
||||
// accessing the state value by destrutcturing the state
|
||||
const { firstName, lastName, title, country } = this.state
|
||||
return (
|
||||
<div className='App'>
|
||||
<h3>Add Student</h3>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<fieldset>
|
||||
<legend>React Form and Form Validation</legend>
|
||||
<div className='row'>
|
||||
<div className='form-group'>
|
||||
<label htmlFor='firstName'>First Name </label>
|
||||
<input
|
||||
type='text'
|
||||
name='firstName'
|
||||
value={this.state.firstName}
|
||||
onChange={this.handleChange}
|
||||
placeholder='First Name'
|
||||
/>
|
||||
</div>
|
||||
<div className='form-group'>
|
||||
<label htmlFor='lastName'>Last Name </label>
|
||||
<input
|
||||
type='text'
|
||||
name='lastName'
|
||||
value={this.state.lastName}
|
||||
onChange={this.handleChange}
|
||||
placeholder='Last Name'
|
||||
/>
|
||||
</div>
|
||||
<div className='form-group'>
|
||||
<label htmlFor='email'>Email </label>
|
||||
<input
|
||||
type='email'
|
||||
name='email'
|
||||
value={this.state.email}
|
||||
onChange={this.handleChange}
|
||||
placeholder='Email'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='form-group'>
|
||||
<label htmlFor='tel'>Telephone </label>
|
||||
<input
|
||||
type='tel'
|
||||
name='tel'
|
||||
value={this.state.tel}
|
||||
onChange={this.handleChange}
|
||||
placeholder='Tel'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='form-group'>
|
||||
<label htmlFor='dateOfBirth'>Date of birth </label>
|
||||
<input
|
||||
type='date'
|
||||
name='dateOfBirth'
|
||||
value={this.state.dateOfBirth}
|
||||
onChange={this.handleChange}
|
||||
placeholder='Date of Birth'
|
||||
/>
|
||||
</div>
|
||||
<div className='form-group'>
|
||||
<label htmlFor='favoriteColor'>Favorite Color</label>
|
||||
<input
|
||||
type='color'
|
||||
id='color'
|
||||
name='color'
|
||||
value={this.state.color}
|
||||
onChange={this.handleChange}
|
||||
placeholder='Favorite Color'
|
||||
/>
|
||||
</div>
|
||||
<div className='form-group'>
|
||||
<label htmlFor='dateOfBirth'>Weight </label>
|
||||
<input
|
||||
type='number'
|
||||
id='weight'
|
||||
name='weight'
|
||||
value={this.state.weight}
|
||||
onChange={this.handleChange}
|
||||
placeholder='Weight in Kg'
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor='country'>Country</label> <br />
|
||||
<select name='country' onChange={this.handleChange} id='country'>
|
||||
{selectOptions}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p>Gender</p>
|
||||
<div>
|
||||
<input
|
||||
type='radio'
|
||||
id='female'
|
||||
name='gender'
|
||||
value='Female'
|
||||
onChange={this.handleChange}
|
||||
checked={this.state.gender === 'Female'}
|
||||
/>
|
||||
<label htmlFor='female'>Female</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
id='male'
|
||||
type='radio'
|
||||
name='gender'
|
||||
value='Male'
|
||||
onChange={this.handleChange}
|
||||
checked={this.state.gender === 'Male'}
|
||||
/>
|
||||
<label htmlFor='male'>Male</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
id='other'
|
||||
type='radio'
|
||||
name='gender'
|
||||
value='Other'
|
||||
onChange={this.handleChange}
|
||||
checked={this.state.gender === 'Other'}
|
||||
/>
|
||||
<label htmlFor='other'>Other</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p>Select your skills</p>
|
||||
<div>
|
||||
<input
|
||||
type='checkbox'
|
||||
id='html'
|
||||
name='html'
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<label htmlFor='html'>HTML</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type='checkbox'
|
||||
id='css'
|
||||
name='css'
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<label htmlFor='css'>CSS</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type='checkbox'
|
||||
id='javascript'
|
||||
name='javascript'
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<label htmlFor='javascript'>JavaScript</label>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor='bio'>Bio</label> <br />
|
||||
<textarea
|
||||
id='bio'
|
||||
name='bio'
|
||||
value={this.state.bio}
|
||||
onChange={this.handleChange}
|
||||
cols='120'
|
||||
rows='10'
|
||||
placeholder='Write about yourself ...'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type='file' name='file' onChange={this.handleChange} />
|
||||
</div>
|
||||
<div>
|
||||
<button>Submit</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
## Form Validation
|
||||
|
||||
## What is validation?
|
||||
|
||||
The action or process of checking or proving the validity or accuracy of something in this case data.
|
||||
|
||||
## What is the purpose of validation
|
||||
|
||||
The main purpose to validation is to get a desired data from users. In addition, to prevent malicious users and data.
|
||||
|
||||
## Validation Types
|
||||
|
||||
Validation can be done in client side or sever side. At the moment, we are using React which is a front end technology and we use client side validation.A validation can implement using HTML5 built-in validation or using JavaScript(using regular expression).
|
||||
|
||||
In the following snippet of code, a validation has been implemented the first field. Try to understand how it works. The onBlur event has been used to check validity when the input is not focused.
|
||||
|
||||
```js
|
||||
// index.js
|
||||
import React, { Component } 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 value={value}> {label}</option>
|
||||
))
|
||||
|
||||
class App extends React.Component {
|
||||
// declaring state
|
||||
state = {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
email: '',
|
||||
country: '',
|
||||
tel: '',
|
||||
dateOfBirth: '',
|
||||
favoriteColor: '',
|
||||
weight: '',
|
||||
gender: '',
|
||||
file: '',
|
||||
bio: '',
|
||||
skills: {
|
||||
html: false,
|
||||
css: false,
|
||||
javascript: false,
|
||||
},
|
||||
touched: {
|
||||
firstName: false,
|
||||
lastName: false,
|
||||
},
|
||||
}
|
||||
handleChange = (e) => {
|
||||
/*
|
||||
// we can get the name and value like this but we can also destructure it from e.target
|
||||
const name = e.target.name
|
||||
const value = e.target.value
|
||||
*/
|
||||
const { name, value, type, checked } = e.target
|
||||
// [variablename] this 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') {
|
||||
this.setState({
|
||||
skills: { ...this.state.skills, [name]: checked },
|
||||
})
|
||||
} else if (type === 'file') {
|
||||
console.log(type, 'cehck here')
|
||||
this.setState({ [name]: e.target.files[0] })
|
||||
} else {
|
||||
this.setState({ [name]: value })
|
||||
}
|
||||
}
|
||||
handleBlur = (e) => {
|
||||
const { name, value } = e.target
|
||||
this.setState({ touched: { ...this.state.touched, [name]: true } })
|
||||
}
|
||||
validate = () => {
|
||||
const errors = {}
|
||||
if (
|
||||
(this.state.touched.firstName && this.state.firstName.length < 3) ||
|
||||
(this.state.touched.firstName && this.state.firstName.length > 12)
|
||||
) {
|
||||
errors.firstName = 'First name must be between 2 and 12'
|
||||
}
|
||||
return errors
|
||||
}
|
||||
handleSubmit = (e) => {
|
||||
// stops the default behavior of form element specifically refreshing of page
|
||||
e.preventDefault()
|
||||
|
||||
const {
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
country,
|
||||
gender,
|
||||
bio,
|
||||
file,
|
||||
skills,
|
||||
} = this.state
|
||||
|
||||
const formattedSkills = []
|
||||
for (const key in skills) {
|
||||
console.log(key)
|
||||
if (skills[key]) {
|
||||
formattedSkills.push(key.toUpperCase())
|
||||
}
|
||||
}
|
||||
const data = {
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
country,
|
||||
gender,
|
||||
bio,
|
||||
file,
|
||||
skills: formattedSkills,
|
||||
}
|
||||
console.log(data)
|
||||
}
|
||||
|
||||
render() {
|
||||
// accessing the state value by destrutcturing the state
|
||||
|
||||
const { firstName } = this.validate()
|
||||
return (
|
||||
<div className='App'>
|
||||
<h3>Add Student</h3>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<fieldset>
|
||||
<legend>React Form</legend>
|
||||
<div className='row'>
|
||||
<div className='form-group'>
|
||||
<label htmlFor='firstName'>First Name </label>
|
||||
<input
|
||||
type='text'
|
||||
name='firstName'
|
||||
value={this.state.firstName}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.handleBlur}
|
||||
placeholder='First Name'
|
||||
/> <br />
|
||||
<small>{firstName}</small>
|
||||
</div>
|
||||
<div className='form-group'>
|
||||
<label htmlFor='lastName'>Last Name </label>
|
||||
<input
|
||||
type='text'
|
||||
name='lastName'
|
||||
value={this.state.lastName}
|
||||
onChange={this.handleChange}
|
||||
placeholder='Last Name'
|
||||
/>
|
||||
</div>
|
||||
<div className='form-group'>
|
||||
<label htmlFor='email'>Email </label>
|
||||
<input
|
||||
type='email'
|
||||
name='email'
|
||||
value={this.state.email}
|
||||
onChange={this.handleChange}
|
||||
placeholder='Email'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='form-group'>
|
||||
<label htmlFor='tel'>Telephone </label>
|
||||
<input
|
||||
type='tel'
|
||||
name='tel'
|
||||
value={this.state.tel}
|
||||
onChange={this.handleChange}
|
||||
placeholder='Tel'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='form-group'>
|
||||
<label htmlFor='dateOfBirth'>Date of birth </label>
|
||||
<input
|
||||
type='date'
|
||||
name='dateOfBirth'
|
||||
value={this.state.dateOfBirth}
|
||||
onChange={this.handleChange}
|
||||
placeholder='Date of Birth'
|
||||
/>
|
||||
</div>
|
||||
<div className='form-group'>
|
||||
<label htmlFor='favoriteColor'>Favorite Color</label>
|
||||
<input
|
||||
type='color'
|
||||
id='color'
|
||||
name='color'
|
||||
value={this.state.color}
|
||||
onChange={this.handleChange}
|
||||
placeholder='Favorite Color'
|
||||
/>
|
||||
</div>
|
||||
<div className='form-group'>
|
||||
<label htmlFor='dateOfBirth'>Weight </label>
|
||||
<input
|
||||
type='number'
|
||||
id='weight'
|
||||
name='weight'
|
||||
value={this.state.weight}
|
||||
onChange={this.handleChange}
|
||||
placeholder='Weight in Kg'
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor='country'>Country</label> <br />
|
||||
<select name='country' onChange={this.handleChange} id='country'>
|
||||
{selectOptions}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p>Gender</p>
|
||||
<div>
|
||||
<input
|
||||
type='radio'
|
||||
id='female'
|
||||
name='gender'
|
||||
value='Female'
|
||||
onChange={this.handleChange}
|
||||
checked={this.state.gender === 'Female'}
|
||||
/>
|
||||
<label htmlFor='female'>Female</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
id='male'
|
||||
type='radio'
|
||||
name='gender'
|
||||
value='Male'
|
||||
onChange={this.handleChange}
|
||||
checked={this.state.gender === 'Male'}
|
||||
/>
|
||||
<label htmlFor='male'>Male</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
id='other'
|
||||
type='radio'
|
||||
name='gender'
|
||||
value='Other'
|
||||
onChange={this.handleChange}
|
||||
checked={this.state.gender === 'Other'}
|
||||
/>
|
||||
<label htmlFor='other'>Other</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p>Select your skills</p>
|
||||
<div>
|
||||
<input
|
||||
type='checkbox'
|
||||
id='html'
|
||||
name='html'
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<label htmlFor='html'>HTML</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type='checkbox'
|
||||
id='css'
|
||||
name='css'
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<label htmlFor='css'>CSS</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type='checkbox'
|
||||
id='javascript'
|
||||
name='javascript'
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<label htmlFor='javascript'>JavaScript</label>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor='bio'>Bio</label> <br />
|
||||
<textarea
|
||||
id='bio'
|
||||
name='bio'
|
||||
value={this.state.bio}
|
||||
onChange={this.handleChange}
|
||||
cols='120'
|
||||
rows='10'
|
||||
placeholder='Write about yourself ...'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type='file' name='file' onChange={this.handleChange} />
|
||||
</div>
|
||||
<div>
|
||||
<button>Submit</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
// we render the JSX element using the ReactDOM package
|
||||
ReactDOM.render(<App />, rootElement)
|
||||
```
|
||||
|
||||
# Exercises
|
||||
|
||||
## Exercises: Level 1
|
||||
|
||||
1. What is the importance of form?
|
||||
2. How many input types do you know?
|
||||
3. Mention at least four attributes of an input element
|
||||
4. What is the importance of htmlFor?
|
||||
5. Write an input type which is not given in the example if there is?
|
||||
6. What is a controlled input?
|
||||
7. How do you bind data in React? The first input field example is data binding in React.
|
||||
8. What is validation?
|
||||
9. What is the event type we use to listen when an input changes?
|
||||
10. What are event types we use to validate an input?
|
||||
|
||||
## Exercises: Level 2
|
||||
|
||||
1. Validate the form given above (a gif image or a video will be provided later)
|
||||
|
||||
## Exercises: Level 3
|
||||
|
||||
|
||||
|
||||
🎉 CONGRATULATIONS ! 🎉
|
||||
|
||||
[<< Day 11](../11_Day_Events/11_events.md) | [Day 13 >>]()
|
@ -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 3
|
||||
|
||||
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,318 @@
|
||||
// index.js
|
||||
import React, { Component } 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 value={value}> {label}</option>
|
||||
))
|
||||
|
||||
class App extends React.Component {
|
||||
// declaring state
|
||||
state = {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
email: '',
|
||||
country: '',
|
||||
tel: '',
|
||||
dateOfBirth: '',
|
||||
favoriteColor: '',
|
||||
weight: '',
|
||||
gender: '',
|
||||
file: '',
|
||||
bio: '',
|
||||
skills: {
|
||||
html: false,
|
||||
css: false,
|
||||
javascript: false,
|
||||
},
|
||||
touched: {
|
||||
firstName: false,
|
||||
lastName: false,
|
||||
},
|
||||
}
|
||||
handleChange = (e) => {
|
||||
/*
|
||||
// we can get the name and value like this but we can also destructure it from e.target
|
||||
const name = e.target.name
|
||||
const value = e.target.value
|
||||
*/
|
||||
const { name, value, type, checked } = e.target
|
||||
// [variablename] this 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') {
|
||||
this.setState({
|
||||
skills: { ...this.state.skills, [name]: checked },
|
||||
})
|
||||
} else if (type === 'file') {
|
||||
console.log(type, 'cehck here')
|
||||
this.setState({ [name]: e.target.files[0] })
|
||||
} else {
|
||||
this.setState({ [name]: value })
|
||||
}
|
||||
}
|
||||
handleBlur = (e) => {
|
||||
const { name, value } = e.target
|
||||
this.setState({ touched: { ...this.state.touched, [name]: true } })
|
||||
}
|
||||
validate = () => {
|
||||
const errors = {}
|
||||
if (
|
||||
(this.state.touched.firstName && this.state.firstName.length < 3) ||
|
||||
(this.state.touched.firstName && this.state.firstName.length > 12)
|
||||
) {
|
||||
errors.firstName = 'First name must be between 2 and 12'
|
||||
}
|
||||
return errors
|
||||
}
|
||||
handleSubmit = (e) => {
|
||||
// stops the default behavior of form element specifically refreshing of page
|
||||
e.preventDefault()
|
||||
|
||||
const {
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
country,
|
||||
gender,
|
||||
bio,
|
||||
file,
|
||||
skills,
|
||||
} = this.state
|
||||
|
||||
const formattedSkills = []
|
||||
for (const key in skills) {
|
||||
console.log(key)
|
||||
if (skills[key]) {
|
||||
formattedSkills.push(key.toUpperCase())
|
||||
}
|
||||
}
|
||||
const data = {
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
country,
|
||||
gender,
|
||||
bio,
|
||||
file,
|
||||
skills: formattedSkills,
|
||||
}
|
||||
console.log(data)
|
||||
}
|
||||
|
||||
render() {
|
||||
// accessing the state value by destrutcturing the state
|
||||
|
||||
const { firstName } = this.validate()
|
||||
return (
|
||||
<div className='App'>
|
||||
<h3>Add Student</h3>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<fieldset>
|
||||
<legend>React Form and Form Validation</legend>
|
||||
<div className='row'>
|
||||
<div className='form-group'>
|
||||
<label htmlFor='firstName'>First Name </label>
|
||||
<input
|
||||
type='text'
|
||||
name='firstName'
|
||||
value={this.state.firstName}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.handleBlur}
|
||||
placeholder='First Name'
|
||||
/>{' '}
|
||||
<br />
|
||||
<small>{firstName}</small>
|
||||
</div>
|
||||
<div className='form-group'>
|
||||
<label htmlFor='lastName'>Last Name </label>
|
||||
<input
|
||||
type='text'
|
||||
name='lastName'
|
||||
value={this.state.lastName}
|
||||
onChange={this.handleChange}
|
||||
placeholder='Last Name'
|
||||
/>
|
||||
</div>
|
||||
<div className='form-group'>
|
||||
<label htmlFor='email'>Email </label>
|
||||
<input
|
||||
type='email'
|
||||
name='email'
|
||||
value={this.state.email}
|
||||
onChange={this.handleChange}
|
||||
placeholder='Email'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='form-group'>
|
||||
<label htmlFor='tel'>Telephone </label>
|
||||
<input
|
||||
type='tel'
|
||||
name='tel'
|
||||
value={this.state.tel}
|
||||
onChange={this.handleChange}
|
||||
placeholder='Tel'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='form-group'>
|
||||
<label htmlFor='dateOfBirth'>Date of birth </label>
|
||||
<input
|
||||
type='date'
|
||||
name='dateOfBirth'
|
||||
value={this.state.dateOfBirth}
|
||||
onChange={this.handleChange}
|
||||
placeholder='Date of Birth'
|
||||
/>
|
||||
</div>
|
||||
<div className='form-group'>
|
||||
<label htmlFor='favoriteColor'>Favorite Color</label>
|
||||
<input
|
||||
type='color'
|
||||
id='color'
|
||||
name='color'
|
||||
value={this.state.color}
|
||||
onChange={this.handleChange}
|
||||
placeholder='Favorite Color'
|
||||
/>
|
||||
</div>
|
||||
<div className='form-group'>
|
||||
<label htmlFor='dateOfBirth'>Weight </label>
|
||||
<input
|
||||
type='number'
|
||||
id='weight'
|
||||
name='weight'
|
||||
value={this.state.weight}
|
||||
onChange={this.handleChange}
|
||||
placeholder='Weight in Kg'
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor='country'>Country</label> <br />
|
||||
<select name='country' onChange={this.handleChange} id='country'>
|
||||
{selectOptions}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p>Gender</p>
|
||||
<div>
|
||||
<input
|
||||
type='radio'
|
||||
id='female'
|
||||
name='gender'
|
||||
value='Female'
|
||||
onChange={this.handleChange}
|
||||
checked={this.state.gender === 'Female'}
|
||||
/>
|
||||
<label htmlFor='female'>Female</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
id='male'
|
||||
type='radio'
|
||||
name='gender'
|
||||
value='Male'
|
||||
onChange={this.handleChange}
|
||||
checked={this.state.gender === 'Male'}
|
||||
/>
|
||||
<label htmlFor='male'>Male</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
id='other'
|
||||
type='radio'
|
||||
name='gender'
|
||||
value='Other'
|
||||
onChange={this.handleChange}
|
||||
checked={this.state.gender === 'Other'}
|
||||
/>
|
||||
<label htmlFor='other'>Other</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p>Select your skills</p>
|
||||
<div>
|
||||
<input
|
||||
type='checkbox'
|
||||
id='html'
|
||||
name='html'
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<label htmlFor='html'>HTML</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type='checkbox'
|
||||
id='css'
|
||||
name='css'
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<label htmlFor='css'>CSS</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type='checkbox'
|
||||
id='javascript'
|
||||
name='javascript'
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<label htmlFor='javascript'>JavaScript</label>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor='bio'>Bio</label> <br />
|
||||
<textarea
|
||||
id='bio'
|
||||
name='bio'
|
||||
value={this.state.bio}
|
||||
onChange={this.handleChange}
|
||||
cols='120'
|
||||
rows='10'
|
||||
placeholder='Write about yourself ...'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type='file' name='file' onChange={this.handleChange} />
|
||||
</div>
|
||||
<div>
|
||||
<button>Submit</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
// we render the JSX element using the ReactDOM package
|
||||
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: 723 KiB |