App.js
// useState์์ useReducer๋ก ๋ณ๊ฒฝ
import { useRef, useMemo, useCallback, useReducer } 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๋ฅผ ์ฌ์ฉ
// 1. changeText
const initialState = {
inputs: { username: "", email: "" },
users: [
{
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
}
]
};
// 1. changeText
// 2. Create
// 3. remove
const myReducer = (state, action) => {
// ์คํํจ์
switch (action.type) {
case "CHANGE_INPUT":
return {
...state,
inputs: { ...state.inputs, [action.name]: action.value }
// dispatch๋ก ๋ณด๋ด๋ฉด action์ผ๋ก ๋ฐ์์ด
};
case "CREATE_USER":
return {
users: [...state.users, action.item], // ๋ฐฐ์ด์ ์์ดํ
์ ์ถ๊ฐ
inputs: initialState.inputs // input ์ด๊ธฐํ
};
case "REMOVE_USER":
return {
...state, // ํ์ฌ ์ํ๋ input๊ณผ user๊ฐ ์์
users: state.users.filter((item) => item.id !== action.id) // ๋ด๊ฐ ๊ณ ๋ฅธ id๊ฐ์ ๋นผ๊ณ ๋ค์ ๋ฐฐ์ด์ ๋ง๋ฆ
};
case "TOGGLE_USER":
return {
...state,
users: state.users.map((item) =>
item.id === action.id ? { ...item, active: !item.active } : item
)
};
default:
return state;
}
};
export default function App() {
// 1. changeText
const [state, dispatch] = useReducer(myReducer, initialState);
// ๋น๊ตฌ์กฐํ ๋น
// const users = state.users;
// const username = state.inputs.username;
// const email = state.inputs.email;
const { users } = state;
const { username, email } = state.inputs;
// useCallback() : ๋ญ๊ฐ ๋ณํ๊ฐ ์์ ๋ ์คํ ๋จ
const changeText = useCallback((event) => {
const { name, value } = event.target;
// setInputs({ ...inputs, [name]: value }); // setInput ๋์ dispatch๋ก ๋ณด๋ด์ผ ํจ
dispatch({
type: "CHANGE_INPUT",
name,
value
});
}, []);
// 2. create
// create๋ฅผ ๋๋ฅผ ๋ ๋ง๋ค id๊ฐ์ด 1์ฉ ์ฆ๊ฐํ๋๋ก ํ๋ ๋ณ์ ์ค์
const nextId = useRef(5); // ๋ฐฐ์ด ์์ดํ
์ ๋ค์ด ๊ฐ id
const focusIn = useRef(); // ํน์ ํ DOM(์๋ฆฌ๋จผํธ) ์ ํ์ ์ํ ์ค์
const onCreate = useCallback(() => {
dispatch({
type: "CREATE_USER",
item: {
id: nextId.current, // key: value
username,
email
}
});
nextId.current += 1;
focusIn.current.focus();
}, [username, email]);
// setUsers([...users, item]); // ๋ฐฐ์ด์ ์์ดํ
์ ๋ฃ๋ ํจ์ ์คํ
// setInputs({ username: "", email: "" }); // input ์ด๊ธฐํ
// 3. ์ญ์ ํจ์ ์ ์
const remove = useCallback((id) => {
dispatch({
type: "REMOVE_USER",
id
});
}, []);
// 4. username ์์ ๋ฐ๊ฟ์ฃผ๋ toggleํจ์
const toggleColor = useCallback((id) => {
dispatch({
type: "TOGGLE_USER",
id
});
/*
setUsers(
users.map(
(item) =>
// item์ {} ํ ๊ฐ๋ฅผ ์๋ฏธ
// item.active = active์ key๊ฐ์ ์๋ฏธ
item.id === id ? { ...item, active: !item.active } : item // ๋ช
๋ น์ด ํ ์ค (์ค๊ดํธ ํ์x)
)
);*/
}, []);
// 5. active ์ ์ฉ๋ ์ด๋ฆ ์ซ์ ์ธ์ฃผ๋ ํจ์ ์คํ
// useMemo(ํจ์, ๋ณํ์์)
const count = useMemo(() => countActiveUsers(users), [users]);
return (
<div>
<CreateUser
username={username}
email={email}
changeText={changeText}
onCreate={onCreate}
focusRef={focusIn}
/>
<br />
<br />
<UserList users={users} remove={remove} toggleColor={toggleColor} />
<hr />
<div>Active ์ฌ์ฉ์ ์ : {count}</div>
</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 React, { useReducer } from "react";
import personReducer from "./person-reducer"; // ์ค๊ดํธ๋ก ๋ฐ์์ฌ๊ฑฐ๋ฉด export ํ ๋ default ์ ์จ๋ ๋จ
import "./styles.css";
export default function App() {
const initialPerson = {
// person์ ์ด๊ธฐ๊ฐ
name: "์ ์งํ",
work: "๊ฐ๋ฐ์",
mentors: [
{
name: "๋ง์ดํด",
work: "์๋์ด๊ฐ๋ฐ์"
},
{
name: "ใ
ใ
ใ
",
work: "ํ์ฅ"
},
{
name: "๊ผฌ๋ง",
work: "๊ฐ์์ง"
}
]
};
const [person, dispatch] = useReducer(personReducer, initialPerson);
// 3. ๋ฉํ ์ด๋ฆ ๋ฐ๊พธ๊ธฐ ํจ์ ์ ์
const updateMento = () => {
const prev = prompt("๋ฐ๊พธ๊ณ ์ถ์ ์ด๋ฆ์?");
const current = prompt("์๋ก ์ง์ ํ ์ด๋ฆ์?");
dispatch({
type: "updated",
prev,
current
});
// setPerson((person) => ({
// ...person, // person ์ ์ฒด ๋์ด(name, work, mentos)
// /*
// ์ค๋ธ์ ํธ ๋จ์๋ก map ๋๋ฆผ
// person.mentors ๋ผ๋ array๋ฅผ map์ ์ฌ์ฉํ์ฌ item{} ๊ธฐ์ค์ผ๋ก ํ๋์ฉ ๊ฒ์ฌ
// */
// mentors: person.mentors.map((item) => {
// // prev๊ฐ์ด ๊ฐ์ ๊ฒ์ด ์๋์ง ํ์ธ. item์ ์ด๋ฆ์ด prev์ ๊ฐ์ผ๋ฉด,
// if (item.name === prev) {
// // name์ ๋์ค์ ์
๋ ฅํ ๊ฐ(current)๋ก ๋ฐ๊ฟ์ค
// return { ...item, name: current };
// }
// return item;
// })
// }));
};
// 2. ๋ฉํ ์ญ์ ํจ์ ์ ์
const deleteMento = () => {
const del = prompt("์ญ์ ํ๊ณ ์ถ์ ์ด๋ฆ์?");
dispatch({
type: "deleted",
del
});
};
// 1. ๋ฉํ ์ถ๊ฐ ํจ์ ์ ์
const addMento = () => {
const name = prompt("์ถ๊ฐํ ๋ฉํ ์ ์ด๋ฆ์?");
const work = prompt("์ถ๊ฐํ ๋ฉํ ์ ์ง์
์?");
// setPerson(() => ({
// ...person,
// mentors: [person.mentors, { name, work }] // ๋ค์ ์ ์ผ๋ฉด ๋ค์ ์ถ๊ฐํ๋ค๋ ๋ป
// }));
dispatch({
type: "added",
name,
work
// action ์์ name๊ณผ work๊ฐ ์์
});
};
return (
<div className="App">
<h1>
{person.name}์ {person.work}์
๋๋ค.
</h1>
<h4>{person.name}์ ๋ฉํ ๋ : </h4>
{/* ์ธ ๋ฒ์งธ ๋ฉํ - {person.mentors[2].name} */}
<ul>
{person.mentors.map((item, index) => (
<li key={index}>
{item.name} ({item.work})
</li>
))}
</ul>
<button onClick={updateMento}>๋ฉํ ์ด๋ฆ ๋ฐ๊พธ๊ธฐ</button>
<button onClick={deleteMento}>๋ฉํ ์ญ์ </button>
<button onClick={addMento}>๋ฉํ ์ถ๊ฐ</button>
</div>
);
}
person-reducer.js
// reducer ํจ์ ์ ์ธ
export default function personReducer(person, action) {
switch (action.type) {
case "updated":
const { prev, current } = action;
return {
...person,
// person.mentors ์ด๋ผ๋ array
mentors: person.mentors.map((mentor) => {
// prev๊ฐ์ด ๊ฐ์ ๊ฒ์ด ์๋์ง ํ์ธ. item์ ์ด๋ฆ์ด prev์ ๊ฐ์ผ๋ฉด,
if (mentor.name === prev) {
// name์ ๋์ค์ ์
๋ ฅํ ๊ฐ(current)๋ก ๋ฐ๊ฟ์ค
return { ...mentor, name: current };
}
return mentor;
})
};
case "added": {
// const name = action.name;
// const work = action.work;
const { name, work } = action;
return {
...person,
mentors: [...person.mentors, { name, work }]
};
}
case "deleted":
return {
...person,
mentors: person.mentors.filter((aa) => aa.name !== action.del) // ๊ฐ๊ฐ์ ์์
};
default:
return person;
// throw Error("์๋ฌ๊ฐ ๋ฐ์ํ์ด์");
}
}
์ถ๊ฐ/์ญ์ /๋ณ๊ฒฝ ๊ฐ๋ฅ
App.js
import Context from "./Context";
import "./styles.css";
export default function App() {
return (
<div className="App">
<Context />
</div>
);
}
Context.jsx
import React, { createContext, useContext, useState } from "react";
// createContext() - context ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ์ค(๋ณ์), ์ปดํฌ๋ํธ์ฒ๋ผ ์ฐ์
// ("๊ธฐ๋ณธ value") - ์ปจํ
์คํธ์์ ์ฌ์ฉ ํ ๊ธฐ๋ณธ๊ฐ
const MyContext = createContext("๊ธฐ๋ณธ value");
function Child() {
const text = useContext(MyContext);
return <div>{text}</div>;
}
function Parent({ text }) {
return <Child />;
}
function GrandParent() {
return <Parent />;
}
function Context() {
const [value, setValue] = useState(true);
return (
<MyContext.Provider value={value ? "good" : "no"}>
<GrandParent />
<button onClick={() => setValue(!value)}>Click Me</button>
</MyContext.Provider>
);
}
export default Context;