App.js
import { useRef, useState, useMemo, useCallback } from "react";
import CreateUser from "./CreateUser";
import "./styles.css";
import UserList from "./UserList";
// active๋ ์์ดํ
์ซ์๋ฅผ ์ธ ์ฃผ๋ ํจ์
// array๋ฅผ ์ธ์๋ก ๋ฐ์์ด
function countActiveUsers(users) {
return users.filter((item) => item.active).length;
}
// ํ์ ์์๋๋ ๋๋ฌด ์์ฃผ ์คํ๋จ (๋ฐ์ดํฐ ๋ญ๋น, ์ฑ๋ฅ ํ๋ฝ) -> useMemo๋ฅผ ์ฌ์ฉ
export default function App() {
const initialUsers = [
{
id: 1,
username: "Bret",
email: "Sincere@april.biz",
active: true
},
{
id: 2,
username: "Antonette",
email: "Shanna@melissa.tv",
active: false
},
{
id: 3,
username: "Samantha",
email: "Nathan@yesenia.net",
active: false
},
{
id: 4,
username: "Karianne",
email: "Julianne.OConner@kory.org",
active: false
}
];
const [users, setUsers] = useState(initialUsers); //๋ฐฐ์ด์ ์ฒด
const [inputs, setInputs] = useState({ username: "", email: "" }); // input(2๊ฐ) ์์ ๋ด์ฉ(value)์ ์์์ ์ฅํ ๋ฐฐ์ด (username, email์ ๊ธฐ์กด ๋ฐฐ์ด์ ์ด๋ฆ๊ณผ ๊ฐ๊ฒ)
// state์์ ์๋ ์ํ. ์ ์๋ ๋ ๋ค ๊ฐ์ ํํ
// const username = inputs.username;
// const email = inputs.email;
const { username, email } = inputs;
const changeText = useCallback(
(event) => {
// const name = event.target.name; // username, email ์ค ํ๋. (์ด๋ค input ์ธ์ง)
// const value = event.target.value; // input์ ์
๋ ฅ ๋ ๊ฐ
const { name, value } = event.target;
setInputs({ ...inputs, [name]: value });
// console.log(inputs);
},
[inputs]
);
// create๋ฅผ ๋๋ฅผ ๋ ๋ง๋ค id๊ฐ์ด 1์ฉ ์ฆ๊ฐํ๋๋ก ํ๋ ๋ณ์ ์ค์
const nextId = useRef(5); // ๋ฐฐ์ด ์์ดํ
์ ๋ค์ด ๊ฐ id
const focusIn = useRef(); // ํน์ ํ DOM(์๋ฆฌ๋จผํธ) ์ ํ์ ์ํ ์ค์
const onCreate = useCallback(
(event) => {
console.log("onCreate ํจ์ ์คํ");
const item = {
id: nextId.current, // key: value
username,
email
};
setUsers([...users, item]); // ๋ฐฐ์ด์ ์์ดํ
์ ๋ฃ๋ ํจ์ ์คํ
setInputs({ username: "", email: "" }); // input ์ด๊ธฐํ
nextId.current += 1;
focusIn.current.focus();
},
[users, username, email]
);
// ์ญ์ ํจ์ ์ ์
const remove = (id) => {
// console.log("์์ด๋๋? : ", id);
setUsers(users.filter((item) => item.id !== id)); // ๋ด๊ฐ ๊ณ ๋ฅธ id๊ฐ์ ๋นผ๊ณ ๋ค์ ๋ฐฐ์ด์ ๋ง๋ฆ
};
// username ์์ ๋ฐ๊ฟ์ฃผ๋ ํ ๊ธํจ์
const toggleColor = (id) => {
setUsers(
users.map(
(item) =>
// item์ {} ํ ๊ฐ๋ฅผ ์๋ฏธ
// item.active = active์ key๊ฐ์ ์๋ฏธ
item.id === id ? { ...item, active: !item.active } : item // ๋ช
๋ น์ด ํ ์ค (์ค๊ดํธ ํ์x)
)
);
};
// active ์ ์ฉ๋ ์ด๋ฆ ์ซ์ ์ธ์ฃผ๋ ํจ์ ์คํ
// useMemo(ํจ์, ๋ณํ์์)
const count = useMemo(() => countActiveUsers(users), [users]);
return (
<div>
<CreateUser
changeText={changeText}
onCreate={onCreate}
username={username}
email={email}
focusRef={focusIn}
/>
<br />
<br />
<UserList users={users} remove={remove} toggleColor={toggleColor} />
<hr />
<div>Active ์ฌ์ฉ์ ์ : {count}</div>
<dl>
<dt>useCallback</dt>
<dd>React Hooks ์ค ํ๋, useMemo์ ๋น์ท</dd>
<dd>useMemo๋ ํน์ ๊ฒฐ๊ณผ๊ฐ์ ์ฌ์ฌ์ฉ, useCallbackํจ์๋ฅผ ์ฌ์ฌ์ฉ </dd>
<dd>์ฌ์ฉ๋ฒ - useCallback(ํจ์์ ์ธ๊ตฌ, ๋ณํ์์(dependency))</dd>
<dd>
ํจ์ ์์์ ์ฌ์ฉํ๋ props๊ฐ ์์ผ๋ฉด ๊ผญ deps๋ก ํฌํจ์์ผ์ผ ํจ ->
์ต์ ๊ฐ์ ์ธ์ํ๊ฒ{" "}
</dd>
</dl>
</div>
);
}
CreateUser.jsx
import React from "react";
export default function CreateUser({
changeText,
onCreate,
username,
email,
focusRef
}) {
return (
<>
<input
type="text"
name="username"
placeholder="์ ์ ๋ค์"
onChange={changeText}
value={username}
ref={focusRef}
/>
<input
type="email"
name="email"
placeholder="์ด๋ฉ์ผ"
onChange={changeText}
value={email}
/>
<button onClick={onCreate}>๋ฑ๋ก</button>
</>
);
}
UserList.jsx
import React from "react";
//์ปดํฌ๋ํธ ์ถ๊ฐ ์ ์
function User({ item, toggleColor, remove }) {
// "item."์ ์์ ๊ธฐ ์ํด ๋ค์ ์ง์
const { id, active, username, email } = item;
return (
<div className={id}>
<b
style={{
color: active ? "orange" : "gray",
cursor: "pointer",
marginRight: 5
}}
onClick={() => toggleColor(id)}
>
{username}
</b>{" "}
<span>({email})</span>
<button
onClick={() => {
remove(id); // ๋ช ๋ฒ์งธ ๊ฐ์ ์ ํํ๋์ง ์์ด๋๊ฐ์ ๋ฐ์์ด
}}
>
์ญ์
</button>
</div>
);
}
// onRemove๋ key๊ฐ์ด ๋์ด๊ฐ
export default function UserList({ users, remove, toggleColor }) {
return (
<>
{users.map((item) => (
// ์ด rr์ด ์ User ํจ์์ rr๋ก ๋์ด๊ฐ
<User
item={item}
key={item.id}
remove={remove}
toggleColor={toggleColor}
/>
))}
</>
);
}
App.js
import "./App.scss";
import { useState } from "react";
import Products from "./Products";
function App() {
const [showProducts, setShowProducts] = useState(true); // ์ด๊ธฐ๊ฐ
return (
<div className="App">
<button onClick={() => setShowProducts((show) => !show)}>
Toggle Button
</button>
{showProducts && <Products />}
<hr />
<div>
<dl>
<dt>useEffect</dt>
<dd>React Hooks์ ์ผ์ข
</dd>
<dd>๋ผ์ดํ์ฌ์ดํด hook(์์ ์ฃผ๊ธฐ) - ๋ง์ดํธ / ์ธ๋ง์ดํธ / ์
๋ฐ์ดํธ(depth์ props)</dd>
<dd>depth๋ฅผ ๋น์ด์๋ ๋ฐฐ์ด๋ก ๋ง๋ค์ด ๋์ผ๋ฉด ์ฒ์ ํ ๋ฒ๋ง ๋ง์ดํธ(๋ก๋ฉ) ๋จ</dd>
<dd>useEffect(์ฝ๋ฐฑํจ์, depth)</dd>
<dd>- ์ฝ๋ฐฑํจ์ ๋ง์ง๋ง ๋ถ๋ถ์ return ํจ์๋ก ์ธ๋ง์ดํธ ์ค์ </dd>
</dl>
</div>
</div>
);
}
export default App;
Products.jsx
import React, { useEffect, useState } from "react";
const Products = () => {
const [products, setProducts] = useState([]); // ๋ถ๋ฌ์จ jsonํ์ผ ์ํ(์ ์ฒด์ ํ)
const [checked, setChecked] = useState(false); // ์ฒดํฌ๋ฐ์ค ์ํ
const change = () => {
setChecked(!checked);
console.log("์ฒดํฌ");
}
useEffect(() => {
// fetch(checked ? "data/sale_products.json" : "data/products.json")
fetch(`data/${checked ? "sale_" : ""}products.json`) // ์์ ๊ฐ์ ์ฝ๋
.then((res) => res.json())
.then((data) => {
// console.log("๋ฐ์ดํฐ๋ฅผ ๋คํธ์ํฌ์์ ๋ฐ์์์๐ฅ : ", data);
setProducts(data); //setProducts๊ฐ ๋ฐ์์จ data๋ฅผ ์์ products์ ์ง์ด๋ฃ์
});
return () => { // ์ธ๋ง์ดํธ ๋ ๋(ํ๋ฉด์์ ์ฌ๋ผ์ง ๋) ์ฝ๋ฐฑํ๋ ํจ์
console.log("toggle ๋ฒํผ ํด๋ฆญ");
}
}, [checked]);
// depth๋ฅผ []๋ฒ ๋ฐฐ์ด๋ก ํด๋์ ๋๋ ์ฒ์ ํ ๋ฒ๋ง ๋ถ๋ฌ์จ๋ค๋ ๋ป
// depth์ checked๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค useEffect ์์ ํจ์๊ฐ ๋ฐ์
return (
<>
{/* htmlFor - react jsx์์ for ๋์ ์ฌ์ฉ */}
<label htmlFor="sale">Show only Sale๐ฅ</label>
<input type="checkbox" name="" id="sale" onChange={change} value={checked}/>
<ul>
{products.map(item => (
<li>
<article>
<img src={item.url} alt="" />
<h3>{item.name}</h3>
<p>{item.price}</p>
</article>
</li>
)
)}
</ul>
</>
);
};
export default Products;
toggle
checkbox
App.js
import Counter from "./Counter";
import "./styles.css";
export default function App() {
return (
<div className="App">
<Counter />
</div>
);
}
Counter.jsx
import React, { useReducer } from "react";
// reducer ํจ์ ์ ์ธ
function reducer(state, action) {
// ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ ๋ก์ง
switch (action.type) {
case "INCREMENT":
return state + 1;
case "DECREMENT":
return state - 1;
default:
// return state;
throw new Error("์๋ฌ ๋ฐ์");
}
}
export default function Counter() {
// ๋๋ฅผ ๋ ๋ง๋ค ์ซ์๊ฐ ์ฆ๊ฐ๋๊ฒ ํจ
const [number, dispatch] = useReducer(reducer, 0);
// const [ํ์ฌ์ํ(state), ์ก์
์ ๋ฐ์์ํค๋ ํจ์] = useReducer(์ฐ๊ฒฐ๋๋ ํจ์, state์ ์ด๊ธฐ๊ฐ);
// 2๊ฐ์ ์ธ์๋ฅผ ๊ฐ์ง
const increase = () => {
dispatch({ type: "INCREMENT" }); // dispatch(์ก์
์ ๋ณด๋ด์ค), type์ ๋ณด๋ด์ค. type์ ๋๋ฌธ์๋ก ์์ฑ
};
const decrease = () => {
dispatch({ type: "DECREMENT" });
};
return (
<div>
<h1>{number}</h1>
<button onClick={increase}>+1</button>
<button onClick={decrease}>-1</button>
</div>
);
}
html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="./style/main.css">
<title>๋ฐ์ํ</title>
</head>
<body>
<div></div>
</body>
</html>
scss
$mobile: "(max-width: 767px)";
$tablet: "(min-width: 768px) and (max-width: 1200px)";
$pc: "(min-width: 1201px)";
div {
width: 200px;
height: 200px;
background: pink;
transition: .3s;
@media #{$mobile} {
background: red;
}
@media #{$tablet} {
background: lightblue;
width: 100px;
height: 100px;
}
@media #{$pc} {
background: greenyellow;
width: 70vw;
height: 70vh;
}
}
pc
tablet
mobile