배열 항목 추가
먼저 항목을 추가하기 위한 인풋과 버튼을 만들어 App.js 에서 불러온다.
//CreateUser.js
import React from "react";
function CreateUser({ username, email, onChange, onCreate }) {
return (
<div>
<input
name="username"
placeholder="계정명"
onChange={onChange}
value={username}
/>
<input
name="email"
placeholder="이메일"
onChange={onChange}
value={email}
/>
<button onClick={onCreate}>등록</button>
</div>
);
}
export default CreateUser;
///App.js
import React, { useRef,useState } from "react";
import CreateUser from "./CreateUser";
import UserList from "./UserList";
function App() {
const users = [
{
id: 1,
username: "Sun",
email: "123@naver.com",
},
{
id: 2,
username: "Jung",
email: "456@naver.com",
},
{
id: 3,
username: "Kim",
email: "789@naver.com",
},
];
const nextId = useRef(4);
return (
<>
<CreateUser></CreateUser>
<UserList users={users}></UserList>
</>
);
}
export default App;
그리고 App.js 에서 CreateUser 에 필요한 props 들을 만들어서 넣어주고 users 의 배열이 컴포넌트의 상태로서 관리할 수 있도록 useState 로 감싸준다.
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: "Sun",
email: "123@naver.com",
},
{
id: 2,
username: "Jung",
email: "456@naver.com",
},
{
id: 3,
username: "Kim",
email: "789@naver.com",
},
]);
const onCreate = () => {
setInputs({
username: "",
email: "",
});
console.log(nextId.current); //4
nextId.current += 1;
};
////
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
></CreateUser>
배열도 객체를 수정할때와 마찬가지로 배열을 복사한 후 값을 덮어씌우는 방식으로 해야한다. sort, splice, push 등은 사용해서는 안되는데 만약 sort, splice, push 등을 반드시 사용해야 한다면 배열을 복사한 후 사용해야한다. 배열의 불변성을 지키면서 항목을 추가하는 spread 연산자를 이용하는 방법과 concat 함수를 이용하는 방법등이 있다. 두 방법다 기존의 배열은 건드리지 않는다는 점에서 동일하다.
///App.js
const onCreate = () => {
const user = {
id: nextId.current,
username,
email,
};
//spread 연산자 이용시
setUsers([...users, user]);
//concat 함수 이용시
setUsers(users.concat(user));
setInputs({
username: "",
email: "",
});
console.log(nextId.current); //4
nextId.current += 1;
};
이제 인풋에 이메일과 이름을 쓰고 등록버튼을 누르면 추가가 가능해진다.
배열 항목 제거
먼저 App.js 에서 삭제 시키는 함수를 만들어 UserList 의 porps 로 넣어준다. 배열에서 특정 요소를 삭제할 때도 불변성을 지켜가며 해야 하는데 이때 filter 를 사용하면 편하다. 쉽게 말해 splice 와 같이 해당 요소를 제거 한다기보다는 filter 로 조건을 주고 걸러내어 새로 배열을 만들어내는 느낌이다.
const onRemove = (id) => {
setUsers(users.filter((users) => users.id !== id));
};
위와 같이 setUsers 에서 filter 를 사용해 users 안의 요소들중 id 값이 파라미터로 받은 id 값과 다른 것만 남기고 다시 배열을 재구성시킨다.
이제 UserList 컴포넌트를 확인해보자. UserList.js 에서는 각 div 마다 click 이벤트에 onRemove 함수가 들어간 버튼을 만들어 주고, 그 버튼에 id 값을 보내주면 된다.
function User({ user, onRemove }) {
return (
<div>
<b>
{user.username} <span>({user.email})</span>
</b>
<button onClick={() => onRemove(user.id)}>삭제</button>
</div>
);
}
function UserList({ users, onRemove }) {
return (
<div>
{users.map((user) => (
<User key={user.id} user={user} onRemove={onRemove}></User>
))}
</div>
);
}
버튼에 click 이벤트를 넣을 때 주의해야할 점이 있는데, 만약 위와 같이 onClick 시 함수를 만들어 함수 내부에서 onRemove 함수를 호출하지 않고 <button onClick={onRemove(user.id)}>
이런 식으로 onRemove 를 바로 호출한다면 클릭이 아닌 렌더링 됨과 동시에 함수가 호출되어 버린다.
배열 항목 수정
이번에는 클릭하면 색상이 바뀌도록 만들어 보려한다. 먼저 배열안의 객체에 active 라는 boolean 값을 주고 그 값에 따라 색상을 지정해준다.
///App.js
{
id: 1,
username: "Sun",
email: "123@naver.com",
active: true,
},
//IserList.js User 컴포넌트
<b
style=
onClick={() => onToggle(user.id)}
>
위에서 보면 b 에 클릭이벤트로 onToggle 이 있는데 onToggle 함수로 active 값을 변경되게 해줬다.
const onToggle = (id) => {
setUsers(
users.map((user) =>
user.id === id ? { ...user, active: !user.active } : user
)
);
};
이때 배열안의 특정 요소만 변경할때도 map 함수를 쓸 수 있다. 위의 함수를 풀이해보자면 user.id 가 파라미터로 받은 id 와 같을 때, 즉 클릭한 요소일때, user.active 의 값을 바꿔주고 아닌 경우는 그대로 유지하는 함수이다. 이때도 불변성을 지키기 위해 spread 함수를 이용해 먼저 복사한 후 값을 변경해주어야 한다.
//App.js
import React, { useRef, useState } 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: "Sun",
email: "123@naver.com",
active: true,
},
{
id: 2,
username: "Jung",
email: "456@naver.com",
active: false,
},
{
id: 3,
username: "Kim",
email: "789@naver.com",
active: false,
},
]);
const nextId = useRef(4);
const onCreate = () => {
const user = {
id: nextId.current,
username,
email,
};
setUsers(users.concat(user));
setInputs({
username: "",
email: "",
});
console.log(nextId.current); //4
nextId.current += 1;
};
const onRemove = (id) => {
setUsers(users.filter((users) => users.id !== id));
};
const onToggle = (id) => {
setUsers(
users.map((user) =>
user.id === id ? { ...user, active: !user.active } : user
)
);
};
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
></CreateUser>
<UserList
users={users}
onRemove={onRemove}
onToggle={onToggle}
></UserList>
</>
);
}
export default App;
//UserList.js
import React from "react";
function User({ user, onRemove, onToggle }) {
return (
<div>
<b
style=
onClick={() => onToggle(user.id)}
>
{user.username} <span>({user.email})</span>
</b>
<button onClick={() => onRemove(user.id)}>삭제</button>
</div>
);
}
function UserList({ users, onRemove, onToggle }) {
return (
<div>
{users.map((user) => (
<User
key={user.id}
user={user}
onRemove={onRemove}
onToggle={onToggle}
></User>
))}
</div>
);
}
export default UserList;