Section 08: Time to Practice - Time to apply what we learned thus far
The goals
๐ช๐ปLet's Practice What We've Learned!
event.preventDefault()
The preventDefault() method cancels the event if it is cancelable, meaning that the default action that belongs to the event will not occur.
For example, this can be useful when:
- Clicking on a "Submit" button, prevent it from submitting a form
- Clicking on a link, prevent the link from following the URL
Note: Not all events are cancelable. Use the cancelable property to find out if an event is cancelable.
Note: The preventDefault() method does not prevent further propagation of an event through the DOM. Use the stopPropagation() method to handle this.
Module CSS
CSS Module example
The CSS in a CSS module is no different than normal CSS, but the extension of the file is different to mark that the file will be processed.
.container {
margin: 3rem auto;
max-width: 600px;
}
import React from "react"
import * as containerStyles from "./container.module.css"
export default function Container({ children }) {
return (
<section className={containerStyles.container}>{children}</section>
)
}
In this example, a CSS module is imported and declared as a JavaScript object called containerStyles. Then, a CSS class from that object is referenced in the JSX className attribute with containerStyles.container, which renders into HTML with dynamic CSS class names like container-module--container--3MbgH.
CSS class
If we want to use CSS class to decorate our file then when we import that CSS file we should import 'classes'
because it's CSS modules
If it was non-module CSS file then we can just write 'import './Card.css'' but since we are using module css it should be different!
We put the exact css decoration item on the exact component we wanna apply.
So in this case, we wanna decorate input tag with our css files.
Note: Always start component names with a capital letter. (์ฌ์ฉ์ ์ ์ ์ปดํฌ๋ํธ)
React treats components starting with lowercase letters as DOM tags. For example, <div /> represents an HTML div tag, but <Welcome /> represents a component and requires Welcome to be in scope.
์ฌ์ฉ์ ์ง์ ์์ฑ ์ฒซ ๋จ๊ณ
๋์ผํ ์์์ ์ฌ๋ฌ ํด๋์ค์ ์ ์ฉํ๋, ๋ค์์ ๊ฐ๋จํ ์์ ๋ก ์์ํ๊ฒ ์ต๋๋ค.
.one {
color: white;
background-color: brown;
margin: 10px;
width: 50px;
height: 50px;
display: inline-block;
}
.two {
color: white;
background-color: black;
margin: 10px;
width: 150px;
height: 70px;
display: inline-block;
}
.three {
color: white;
background-color: brown;
margin: 10px;
width: 75px;
}
.four {
color: white;
background-color: brown;
margin: 10px;
width: 100px;
}
.five {
background-color: brown;
}
HTML์ ์ ์ฉํด๋ณด๊ฒ ์ต๋๋ค.
<div>
<div class="one">1:</div>
<div class="two">2: Text <span class="five">5 - more text</span></div>
<input class="three">
<textarea class="four">4: Lorem Ipsum</textarea>
</div>
๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
๋ฐ๋ณต๋๋ CSS์ ์ฃผ๋ชฉํด๋ณด์ธ์. ๋ฐฐ๊ฒฝ ์์ ์ฌ๋ฌ ๊ณณ์์ brown์ผ๋ก ์ง์ ํ๊ณ ์์ต๋๋ค. ์ผ๋ถ CSS ์ ์ธ์ ๊ฒฝ์ฐ ๋ ์์ ๋จ๊ณ๋ก ๋ฐ๋ณต๋๋ ํญ๋ชฉ์ ์ฎ๊ฒจ์ CSS์ ์์์ ํตํด ์์ฐ์ค๋ฝ๊ฒ ํด๊ฒฐํ ์๋ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ๋ณด๋ค ๋ณต์กํ ํ๋ก์ ํธ์ ๊ฒฝ์ฐ ํญ์ ์ด๋ ๊ฒ ํ ์ ์๋ ๊ฒ์ ์๋๋๋ค. ์ด ๋ :root ์์ฌ ํด๋์ค์ ์ฌ์ฉ์ ์ง์ ์์ฑ์ ์ ์ธํ๊ณ , ํ์ํ ๊ณณ์์ ๊ทธ ์์ฑ์ ์ฐธ์กฐํจ์ผ๋ก์จ ๋ฐ๋ณต ์ฝ๋์ ํ์๋ฅผ ์ค์ผ ์ ์์ต๋๋ค.
CSS Module
์ด๋ฒ์๋ CSS Module ์ด๋ผ๋ ๊ธฐ์ ์ ๋ํด์ ์์๋ด ์๋ค. ๋ฆฌ์กํธ ํ๋ก์ ํธ์์ ์ปดํฌ๋ํธ๋ฅผ ์คํ์ผ๋ง ํ ๋ CSS Module ์ด๋ผ๋ ๊ธฐ์ ์ ์ฌ์ฉํ๋ฉด, CSS ํด๋์ค๊ฐ ์ค์ฒฉ๋๋ ๊ฒ์ ์๋ฒฝํ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
CRA ๋ก ๋ง๋ ํ๋ก์ ํธ์์ CSS Module ๋ฅผ ์ฌ์ฉ ํ ๋์๋, CSS ํ์ผ์ ํ์ฅ์๋ฅผ .module.css ๋ก ํ๋ฉด ๋๋๋ฐ์, ์๋ฅผ ๋ค์ด์ ๋ค์๊ณผ ๊ฐ์ด Box.module.css ๋ผ๋ ํ์ผ์ ๋ง๋ค๊ฒ ๋๋ค๋ฉด
Box.module.css
.Box {
background: black;
color: white;
padding: 2rem;
}
๋ฆฌ์กํธ ์ปดํฌ๋ํธ ํ์ผ์์ ํด๋น CSS ํ์ผ์ ๋ถ๋ฌ์ฌ ๋ CSS ํ์ผ์ ์ ์ธํ ํด๋์ค ์ด๋ฆ๋ค์ด ๋ชจ๋ ๊ณ ์ ํด์ง๋๋ค. ๊ณ ์ CSS ํด๋์ค ์ด๋ฆ์ด ๋ง๋ค์ด์ง๋ ๊ณผ์ ์์๋ ํ์ผ ๊ฒฝ๋ก, ํ์ผ ์ด๋ฆ, ํด๋์ค ์ด๋ฆ, ํด์ฌ๊ฐ ๋ฑ์ด ์ฌ์ฉ ๋ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด์ Box ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ ๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํ๋๋ฐ์
Box.js
import React from "react";
import styles from "./Box.module.css";
function Box() {
return <div className={styles.Box}>{styles.Box}</div>;
}
export default Box;
className ์ ์ค์ ํ ๋์๋ styles.Box ์ด๋ ๊ฒ import๋ก ๋ถ๋ฌ์จ styles ๊ฐ์ฒด ์์ ์๋ ๊ฐ์ ์ฐธ์กฐํด์ผ ํฉ๋๋ค.
ํด๋์ค ์ด๋ฆ์ ๋ํ์ฌ ๊ณ ์ ํ ์ด๋ฆ๋ค์ด ๋ง๋ค์ด์ง๊ธฐ ๋๋ฌธ์, ์ค์๋ก CSS ํด๋์ค ์ด๋ฆ์ด ๋ค๋ฅธ ๊ด๊ณ ์๋ ๊ณณ์์ ์ฌ์ฉํ CSS ํด๋์ค ์ด๋ฆ๊ณผ ์ค๋ณต๋๋ ์ผ์ ๋ํ์ฌ ๊ฑฑ์ ํ ํ์๊ฐ ์์ต๋๋ค.
์ด ๊ธฐ์ ์ ๋ค์๊ณผ ๊ฐ์ ์ํฉ์ ์ฌ์ฉํ๋ฉด ์ ์ฉํฉ๋๋ค.
- ๋ ๊ฑฐ์ ํ๋ก์ ํธ์ ๋ฆฌ์กํธ๋ฅผ ๋์ ํ ๋ (๊ธฐ์กด ํ๋ก์ ํธ์ ์๋ CSS ํด๋์ค์ ์ด๋ฆ์ด ์ค๋ณต๋์ด๋ ์คํ์ผ์ด ๊ผฌ์ด์ง ์๊ฒ ํด์ค๋๋ค.)
- CSS ํด๋์ค๋ฅผ ์ค๋ณต๋์ง ์๊ฒ ์์ฑํ๊ธฐ ์ํ์ฌ CSS ํด๋์ค ๋ค์ด๋ฐ ๊ท์น์ ๋ง๋ค๊ธฐ ๊ท์ฐฎ์ ๋
form, label, input, div ๋ฑ ๊ธฐ๋ณธ์ ์ธ html์ปดํฌ๋ํธ๋ค์ ๋ชจ๋ ๋ฆฌ์กํธ์ ์ํด ๋ฏธ๋ฆฌ ์ค์ ๋ ๊ฒ์ผ๋ก Props๋ผ๋ ํด๋์ค ์ด๋ฆ๊ณผ ํจ๊ป ์๋ํ๊ณ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ ๋๋ง๋ html์ ์ ์ ํ cssํด๋์ค๋ฅผ ์ ์ฉํจ. ํ์ง๋ง ์ฌ์ฉ์ ์ ์ ์ปดํฌ๋ํธ๋ค์ ์ด className์ด๋ผ๋ ์์ฑ๊ณผ ์ด๋ป๊ฒ ์์ ํด์ผ ํ๋์ง๋ฅผ ๋ชจ๋ฆ. ์๋ํ๋ฉด ๊ทธ๊ฒ์ด ๋ด์ฅ๋ html์ปดํฌ๋ํธ๊ฐ ์๋๋ผ ์ฌ์ฉ์ ์ ์ ์ปดํฌ๋ํธ์ด๊ธฐ ๋๋ฌธ-> ์ฌ์ฉ์ ์ ์ ์ปดํฌ๋ํธ๋ ์ผ๋จ ๋๋ฌธ์๋ก ๋ง๋ค์ด์ค์ผํจ
๊ทธ๋์ Card์ปดํฌ๋ํธ์ ๊ฐ์ className์์ฑ์ ๋ฐ์๋ค์ฌ์ ์ฒ๋ฆฌํ ์ ์๋๋ก ๋ง๋ค์ด์ค์ผํจ
-> ๊ฒฐ๊ตญ ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ฒ์ className์ ๋์์ผ๋ก div์ ์ ์ฉํ๊ณ ์๋ cssํด๋์ค๊ฐ ์ด๋ฏธ ์ ์ฉํ๊ณ ์๋ cardํด๋์ค์ ๋ฐ์๋๋ ๊ฒ๋ฟ๋ง ์๋๋ผ Card์ปดํฌ๋ํธ์ className props์ ์ ์ฌ์ ์ผ๋ก ๋ค์ด์ค๋ ํด๋์ค์๋ ์ ์ฉ๋๋๋ก ํ๋ ๊ฒ
So we do like this to make one coming from the card module CSS file, and one coming from outside, from the props, potentially coming via props.
We used className but it can be any name like above.
If we wanna use some CSS codes that we already used somewhere then if we can just import it then it's easier for us not to write that code again. But if we just simply import it to one component and the other components would not know they also need to change the looking of it, in this case, we can make them refer to props and get it.
tip!
If we import Javascript file into the same Javascript file then we don't have to put file format (.js) but if it's different then we should specify it. (.css)
And now we will get the user input and get the data from it and make a list with using those data.
How can we do?
We will use 'useState' which detects the change of state and work with it.
const AddUser = (props) => {
const [enteredUsername, setEnteredUsername] = useState("");
const [enteredAge, setEnteredAge] = useState("");
const addUserHandler = (event) => {
event.preventDefault();
if (enteredUsername.trim().length === 0 || enteredAge.trim().length === 0) {
return;
// If we return here then the next code doesn't work
}
if (+enteredAge < 1) {
return;
// like -1, -2...
// by using '+' it will be secured to be number
}
console.log(enteredUsername, enteredAge);
setEnteredUsername("");
setEnteredAge("");
};
const usernameChangeHandler = (event) => {
setEnteredUsername(event.target.value);
};
const ageChangeHandler = (event) => {
setEnteredAge(event.target.value);
};
return (
<Card className={classes.input}>
<form onSubmit={addUserHandler}>
<label htmlFor="username">Username</label>
<input
id="username"
type="text"
value={enteredUsername}
onChange={usernameChangeHandler}
/>
<label htmlFor="age">Age (years)</label>
<input
id="age"
type="number"
value={enteredAge}
onChange={ageChangeHandler}
/>
<Button type="submit" onClick="">
Add User
</Button>
</form>
</Card>
);
};
Because we didn't give any props to the 'UsersList' component so it's empty now = undefined
We send our 'users' as a list form like this.
Why we put in app? because if we put this in AddUser component it seems a bit weird.
AddUser component sounds like it will only work to add user's data and output it.
So we can use 'map' method to make a new array.
์ด์ ์ฐ๋ฆฌ๋ usersList์ ์ํ๋ฅผ ๊ด๋ฆฌํด์ผํจ. ์๋? ์ฒ์์๋ ๋น ์ด๋ ์ด๊ฒ ์ง๋ง ๋์ค์ ์์ดํ ๋ค์ด ์ฑ์์ง๋ฉด ๊ทธ ์ด๋ ์ด๋ฅผ ์ฌ์ฉํด์ ๋ฐ์ ๋ฆฌ์คํธ๋ฅผ ๋ณด์ฌ์ค์ผํ๊ธฐ ๋๋ฌธ์. ๊ทธ๋ฐ๋ฐ ์ด๋์ ์ด ๋ฆฌ์คํธ์ ์ ๊ทผํ๊ณ , ์ด๋์ ๊ด๋ฆฌ๋ฅผ ํด์ผํ ๊น?
์ ๊ทผ์ ์ผ๋จ usersList๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ ๊ณณ์์ ์ ๊ทผํด์ผ ํ๋๋ฐ ์ด๋ AddUser์ปดํฌ๋ํธ์์ ๊ฐ๋ฅํ๋ค. ๊ทธ๋ ๋ค๊ณ ํด์ list๋ฅผ adduser์ปดํฌ๋ํธ์ ๋ฐฐ์นํด๋ฒ๋ฆฌ๋ฉด users์ ์ํ๋ฅผ adduser์ปดํฌ๋ํธ์์ ๊ด๋ฆฌํด์ผํ๋๋ฐ ์ด๋ ๋นํจ์จ์ . ์๋๋ฉด users๋ผ๋ props๊ฐ ํ์ํ๊ฒ์ list์ปดํฌ๋ํธ๋๊น.
๊ทธ๋ฌ๋ฉด ์ด๋ป๊ฒ users์ ์ ๊ทผํด์ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ฌ ์ ์์๊น
์ฐ๋ฆฌ๋ app.js์์ users์ ์ํ๋ฅผ ๊ด๋ฆฌํ๋๋ก ํ ๊ฒ. ์๋๋ฉด ์ด ์ปดํฌ๋ํธ๊ฐ AddUser์ UsersList์ปดํฌ๋ํธ๋ณด๋ค ํ ๋จ๊ณ ์์ ์๊ณ ์ด ๋ ์ปดํฌ๋ํธ ์์ ์์ผ๋ฉด์ ๊ฐ์ฅ ๊ฐ๊น์ด ์ปดํฌ๋ํธ์ด๊ณ ๋ ์ปดํฌ๋ํธ์๋ ์ ๊ทผ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ.
And it can be changed like this!
Don't forget that when we make a list with React, we need to give 'Key' on the list!
And we made modal for the error screen.
Where should we render this error modal?
=> It should be rendered in user component
why?
Because ultimately it will be this component that will trigger the modal.
We can may argue that it's general overlay over the entire UI and therfore logically it should be rendered as high as possible in the component tree.
Something like inside of the app component or anything like that.
Now our job is that we should show this modal for only some specific situation when we have errors.