CreateUser.js
import React from "react";
function CreateUser({ username, email, onChange, onCreate }) {
return (
<div>
<input
name="username"
placeholder="계정명"
value={username}
onChange={onChange}
/>
<input
name="email"
placeholder="이메일"
value={email}
onChange={onChange}
/>
<button onClick={onCreate}>등록</button>
</div>
);
}
export default CreateUser;
상태 관리를 부모 컴포넌트인 App에서 하게 하고, input의 값 및 이벤트로 등록할 함수들을 props로 넘겨받아서 사용한다.
App.js
import React, { useState, useRef } from "react";
import CreateUser from "./CreateUser";
import UserList from "./UserList";
function App() {
const [inputs, setInputs] = useState({
username: "",
email: "",
// 초기값 공백 설정
});
const { username, email } = inputs; // 미리 추출
const onChange = (e) => {
const { name, value } = e.target;
// name과 value를 e.target에서 가져오도록!!
setInputs({
...inputs, // 기존 배열 복사!!!
[name]: value,
// 받아온 name값을 value로 덮어 씌운다!
// (name이 가리키고 있는 게 username이면 username을 바꾸고,
// email이면 email을 바꾼다)
});
};
const [users, setUsers] = useState([
{
id: 1,
username: "su",
email: "susu@gmail.com",
},
{
id: 2,
username: "liz",
email: "lili@gmail.com",
},
{
id: 3,
username: "ro",
email: "ro@gmail.com",
},
]);
const nextId = useRef(4); // 이 값이 바뀐다고 해서 굳이 컴포넌트가 리렌더링 될 필요가 없기 때문에 useRef 사용
const onCreate = () => {
// 버튼이 클릭될 때 input에 있는 값을 지우도록
setInputs({
username: "",
email: "",
});
console.log(nextId.current); //4
nextId.current += 1;
};
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} />
</>
);
}
export default App;
배열을 변화시킬 때도 객체와 마찬가지로 불변성을 지켜주어야 하기 때문에, 배열의 push, splice, sort 같은 함수를 사용하면 안된다. 만약 꼭 사용해야 하는 경우에는, 기존의 배열을 한번 복사한 후에 사용해야 한다.
import React, { useState, useRef } from "react";
import CreateUser from "./CreateUser";
import UserList from "./UserList";
function App() {
const [inputs, setInputs] = useState({
username: "",
email: "",
});
const { username, email } = inputs; // 미리 추출
const onChange = (e) => {
const { name, value } = e.target;
// 이 부분 까먹지 말기!! name과 value를 e.target에서 가져오도록!!
setInputs({
...inputs, // 이 부분도!! 기존 배열 복사!!!
[name]: value,
// 받아온 name값을 value로 덮어 씌운다! (name이 가리키고 있는 게 username이면 username을 바꾸고, email이면 email을 바꾼다)
});
};
const [users, setUsers] = useState([
{
id: 1,
username: "su",
email: "susu@gmail.com",
},
{
id: 2,
username: "liz",
email: "lili@gmail.com",
},
{
id: 3,
username: "ro",
email: "ro@gmail.com",
},
]);
const nextId = useRef(4); // 이 값이 바뀐다고 해서 굳이 컴포넌트가 리렌더링 될 필요가 없기 때문에 useRef 사용
const onCreate = () => {
// 버튼이 클릭될 때 input에 있는 값을 지우도록
const user = {
// 새로운 유저 객체 생성
id: nextId.current,
username,
email,
};
setUsers([...users, user]);
// 기존배열복사 후, 새로운 배열 만듬
setInputs({
username: "",
email: "",
});
nextId.current += 1;
};
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} />
</>
);
}
export default App;
contcat
함수는 기존 배열을 수정하는 것이 아니라 여러 개의 배열을 하나의 배열로 합쳐준다.
const onCreate = () => {
// 버튼이 클릭될 때 input에 있는 값을 지우도록
const user = {
// 새로운 유저 객체 생성
id: nextId.current,
username,
email,
};
// setUsers([...users, user]);
setUsers(users.concat(user));
setInputs({
username: "",
email: "",
});
nextId.current += 1;
};
import React from "react";
function User({ user, onRemove }) {
const { username, email, id } = user;
return (
<div>
<b>{username}</b> <span>{email}</span>
<button onClick={() => onRemove(id)}>삭제</button>
{/* 이 버튼이 눌렸을 때 새로운 함수를 만드는데,
id가 특정값인 onRemove 함수 실행하는 함수 */}
</div>
);
}
export default function UserList({ users, onRemove }) {
return (
<div>
{users.map((user) => (
<User user={user} key={user.id} onRemove={onRemove} />
))}
</div>
);
}
삭제 버튼이 클릭될 때, user.id값을 props로 받아올 onRemove
함수의 파라미터로 넣어서 호출해줘야 한다.
불변성을 지키면서 배열에서 특정 원소를 제거하기 위해서는 filter
배열 내장 함수를 사용하는 것이 편리하다. 이 함수를 사용하면 배열에서 특정 조건을 만족하는 원소들만 추출하여 새로운 배열을 만들 수 있다.
import React, { useState, useRef } from "react";
import CreateUser from "./CreateUser";
import UserList from "./UserList";
function App() {
const [inputs, setInputs] = useState({
username: "",
email: "",
});
const { username, email } = inputs; // 미리 추출
const onChange = (e) => {
const { name, value } = e.target;
setInputs({
...inputs,
[name]: value,
});
};
const [users, setUsers] = useState([
{
id: 1,
username: "su",
email: "susu@gmail.com",
},
{
id: 2,
username: "liz",
email: "lili@gmail.com",
},
{
id: 3,
username: "ro",
email: "ro@gmail.com",
},
]);
const nextId = useRef(4);
const onCreate = () => {
// 버튼이 클릭될 때 input에 있는 값을 지우도록
const user = {
// 새로운 유저 객체 생성
id: nextId.current,
username,
email,
};
// 기존배열복사 후, 새로운 배열
setUsers(users.concat(user));
setInputs({
username: "",
email: "",
});
nextId.current += 1;
};
const onRemove = (id) => {
setUsers(users.filter((user) => user.id !== id));
// users 배열에 filter를 걸어주고,
// 각 user 객체를 확인하는데
// 그중 user.id가 파라미터로 가져온 id와 일치하지 않는 원소만 추출해서 새로운 배열을 만듬
// 파라미터가 일치하면, 값이 false가 되면서 해당 배열에서 제외
};
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onRemove={onRemove} />
</>
);
}
export default App;
onClick
에서 아래와 같이 onRemove()
함수를 따로 호출하는 함수를 새로 만들어주지 않으면, 렌더링 되는 순간 onRemove
함수가 바로 실행되어 버린다. 따라서 반드시 함수를 따로 만들어 넣어주어야 한다.
<button onClick={onRemove(id)}>삭제</button>
X
<button onClick={() => onRemove(id)}>삭제</button>
계정명을 클릭했을 때 초록색으로 바뀌고 다시 누르면 검정색으로 바뀌도록 구현한다.
(1) 배열의 객체 안에 active 속성 추가
const [users, setUsers] = useState([
{
id: 1,
username: "su",
email: "susu@gmail.com",
active: true,
},
{
id: 2,
username: "liz",
email: "lili@gmail.com",
active: false,
},
{
id: 3,
username: "ro",
email: "ro@gmail.com",
active: false,
},
]);
(2) User컴포넌트에서 active값에 따라 폰트 색상이 바뀌도록, 커서 포인터로 변경
function User({ user, onRemove, onToggle }) {
const { username, email, id, active } = user;
return (
<div>
<b
style={{
color: active ? "green" : "black",
cursor: "pointer",
}}
>
{username}
</b>
<span>{email}</span>
<button onClick={() => onRemove(id)}>삭제</button>
{/* 이 버튼이 눌렸을 때 새로운 함수를 만드는데, id가 특정값인 onRemove 함수 실행하는 함수 */}
</div>
);
}
(3) App.js에서 onToggle
함수 구현
배열 안에 원소를 업데이트할 때에도 map함수를 사용할 수 있다.
유저 객체를 확인하는데, 각 유저 객체의 아이디가 파라미터로 받은 아이디와 일치할 경우, 유저를 복사해서 기존 유저가 들고 있던 값을 넣어주고, 특정 값(active)만 업데이트한다. 일치하지 않을 경우 그대로 둔다.
import React, { useState, useRef } from "react";
import CreateUser from "./CreateUser";
import UserList from "./UserList";
function App() {
const [inputs, setInputs] = useState({
username: "",
email: "",
});
const { username, email } = inputs; // 미리 추출
const onChange = (e) => {
const { name, value } = e.target;
// 이 부분 까먹지 말기!! name과 value를 e.target에서 가져오도록!!
setInputs({
...inputs, // 이 부분도!! 기존 배열 복사!!!
[name]: value,
// 받아온 name값을 value로 덮어 씌운다! (name이 가리키고 있는 게 username이면 username을 바꾸고, email이면 email을 바꾼다)
});
};
const [users, setUsers] = useState([
{
id: 1,
username: "su",
email: "susu@gmail.com",
active: true,
},
{
id: 2,
username: "liz",
email: "lili@gmail.com",
active: false,
},
{
id: 3,
username: "ro",
email: "ro@gmail.com",
active: false,
},
]);
const nextId = useRef(4); // 이 값이 바뀐다고 해서 굳이 컴포넌트가 리렌더링 될 필요가 없기 때문에 useRef 사용
const onCreate = () => {
// 버튼이 클릭될 때 input에 있는 값을 지우도록
const user = {
// 새로운 유저 객체 생성
id: nextId.current,
username,
email,
};
// setUsers([...users, user]);
// 기존배열복사 후, 새로운 배열
setUsers(users.concat(user));
setInputs({
username: "",
email: "",
});
nextId.current += 1;
};
const onRemove = (id) => {
setUsers(users.filter((user) => user.id !== id));
// users 배열에 filter를 걸어주고, 각 user 객체를 확인하는데 그중에서 user.id가 파라미터로 가져온 아이디와 일치하지 않는 것들만 추출
// 그러면 파라미터가 일치하면, 값이 false가 되면서 해당 배열에서 제외된다.
};
const onToggle = (id) => {
setUsers(
user.map((user) =>
user.id === id ? { ...user, active: !user.active } : user
)
);
};
// 배열안에 원소를 업데이트할 때에도 map함수를 사용
// 유저 객체를 확인하는데, 각 유저 객체의 id가 파라미터로 받은 id와 일치할 경우,
// 유저를 복사해서 기존 유저가 들고 있던 값을 넣어주고, 특정 값(active)만 반전시킴
// 일치하지 않을 경우 그대로
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle} />
</>
);
}
export default App;
(4) onToggle
함수 받아와 User에게 전달, onToggle
에 id
넣어서 호출
import React from "react";
function User({ user, onRemove, onToggle }) {
const { username, email, id, active } = user;
return (
<div>
<b
style={{
color: active ? "green" : "black",
cursor: "pointer",
}}
onClick={() => onToggle(id)}
>
{username}
</b>
<span>{email}</span>
<button onClick={() => onRemove(id)}>삭제</button>
{/* 이 버튼이 눌렸을 때 새로운 함수를 만드는데, id가 특정값인 onRemove 함수 실행하는 함수 */}
</div>
);
}
export default function UserList({ users, onRemove, onToggle }) {
return (
<div>
{users.map((user) => (
<User
user={user}
key={user.id}
onRemove={onRemove}
onToggle={onToggle}
/>
))}
</div>
);
}