배열에 항목 추가하기 (Create)
배열에 변화를 줄 때에는 객체와 마찬가지로 불변성을 지켜야 한다.
따라서 push, splice, sort 등의 함수는 사용하면 안된다.
사용해야 한다면 기존의 배열을 한 번 복사하고 나서 사용해야 한다.
불변성을 지키면서 배열에 새 항목을 추가하는 방법
- spread 연산자 사용 ([...복사할배열이름, 배열이름])
setUsers([...users, user])
- 기존의 배열을 수정하지 않고 복사해온 뒤, 값을 설정해준다.
- concat 함수 사용 (배열이름.concat(합치고 싶은 배열))
setUsers(users.concat(user))
- 기존의 배열에 변화를 주지 않으면서, 배열을 합칠 때 새로운 배열을 만들어낸다.
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 UserList from './UserList';
import CreateUser from './CreateUser';
function App() {
const [inputs, setInputs] = useState({
username: '',
email: '',
});
const { username, email } = inputs; // 비구조화 할당
const onChange = e => {
const { name, value } = e.target;
setInputs({
...inputs, // spread 연사자. 기존의 inputs 내용을 복사해준다.
[name]: value // 만약 username 값을 받아온다면 username: value 이런 식으로 값을 재설정
});
};
// users 리스트를 컴포넌트 상태 관리를 위해 useState로 감싸준다.
const [users, setUsers] = useState([
{
id: 1,
username: 'velopert',
email: 'public.velopert@gmail.com'
},
{
id: 2,
username: 'tester',
email: 'tester@example.com'
},
{
id: 3,
username: 'liz',
email: 'liz@example.com'
}
]);
// useRef: 렌더링되지 않고도 관리하고 있는 변수 바로 조회 가능
// useRef( )의 파라미터에는 .current 값의 기본값이 된다.
const nextId = useRef(4);
const onCreate = () => {
// 리스트에 추가할 유저 정보
// 함수가 호출되면 유저리스트에 추가(생성) 후 input 창을 비워준다.
setInputs({
username: '',
email: ''
});
nextId.current += 1;
};
return (
<>
{/* CreateUser에 넘겨줄 Props 값을 태그 내부에 다음과 같이 작성해준다. */}
{/* <CreateUser 넘겨줄props의이름=props값 /> */}
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} />
</>
);
}
export default App;
유저 정보를 추가할 때 불변성을 지키면서 배열에 추가하는 두 가지 방법이 있다.
1. spread
const onCreate = () => {
// 유저리스트(배열)에 추가해줄 유저 정보 user (id-이름-이메일)
const user = {
id: nextId.current,
username,
email,
};
setUsers([...users, user]); // 상태관리하고 있는 기존의 users를 복사해온 뒤, user 를 추가해준다.
...
};
2. concat
const onCreate = () => {
// 유저리스트(배열)에 추가해줄 유저 정보 user (id-이름-이메일)
const user = {
id: nextId.current,
username,
email,
};
setUsers(users.concat(user)); // 새로운 항목(배열)을 만들어 뒤에 user 를 붙여준다.
...
};
배열에 항목 제거하기 (Delete)
배열을 제거할 때도 마찬가지로 불변성을 지켜가면서 업데이트를 해주어야 한다.
UserList.js
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>
</div>
);
}
function UserList({ users, onRemove }) {
return (
<div>
{users.map(user => (
<User
user={user}
key={user.id}
onRemove={onRemove}
/>
))}
</div>
);
}
export default UserList;
🚨 함수 호출 시 주의할 점!
<button onClick={() => onRemove(id)}>삭제</button>
삭제 버튼 클릭 시 파라미터에 전달해주어 onClick
에 새로운 함수를 만들어서 onRemove
를 호출 할 수 있도록 만들었다.
❌ <button onClick={onRemove(id)}>삭제</button>
이렇게 함수가 호출되도록 작성하면 컴포넌트가 렌더링 될 때 onRemove
가 호출되면서 렌더링 되는 시점에 삭제가 실행되며, 유저 리스트가 모두 삭제된다.
Filter
Filter 함수는 특정 조건을 만족하는 원소만 추출해서 새로운 배열을 만든다.
즉, 제거하고자 한다면 제거하려고 하는 원소를 제외한 나머지 배열들만 묶어서 새로 추출하면 불변성을 지켜줄 수 있다.
user.id
가 파라미터(제거하려는 원소)와 일치하지 않는 원소만 추출해서 새로운 배열을 만든다.
useState
를 통해 새로 만들어진 배열을 업데이트 시켜준다.
const onRemove = id => {
setUsers(users.filter(user => user.id !== id));
}
배열에 항목 수정하기 (Update)
배열을 업데이트할 때에도 배열의 불변성을 유지하면서 수정해주어야 한다.
불변성을 지킨다는것은 state 값을 유지한다고 생각하며 된다.
배열 내 특정 아이템만 업데이트 하고자 할 때 map
을 사용할 수 있다.
Map
id
가 일치한다면 업데이트하고 일치하지 않는다면 기존을 그대로 유지하도록 업데이트 한다.
const onToggle = id => {
setUsers(users.map(
user => user.id == id
? {...user, active: !user.active } // 일치하면 active 반전시킴
: user // 일치하지 않으면 기존의 값 유지
))
}
계정명을 클릭했을 때 색상이 초록색으로 바뀌고 다시 누르면 검정색으로 바뀌도록 구현하고자 한다.
App.js 에서 users
배열 내부의 객체들에 active
라는 값을 추가해준 뒤, UserList.js에서 active
를 props
로 받아온다.
그리고 username
에 스타일을 작성해준다.
active
가 true라면 초록색, false라면 검정색을 보여준다.
계정명을 눌렀을 때 함수가 호출되도록 해줄 것이다.
function User({ user, onRemove }) {
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>
</div>
)
}
전체 코드
App.js
import React, { useRef, useState } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';
function App() {
const [inputs, setInputs] = useState({
username: '',
email: '',
});
const { username, email } = inputs; // 비구조화 할당
const onChange = e => {
const { name, value } = e.target;
setInputs({
...inputs, // spread 연사자. 기존의 inputs 내용을 복사해준다.
[name]: value // 만약 username 값을 받아온다면 username: value 이런 식으로 값을 재설정
});
};
// users 리스트를 컴포넌트 상태 관리를 위해 useState로 감싸준다.
const [users, setUsers] = useState([
{
id: 1,
username: 'velopert',
email: 'public.velopert@gmail.com',
active: true,
},
{
id: 2,
username: 'tester',
email: 'tester@example.com',
active: true,
},
{
id: 3,
username: 'liz',
email: 'liz@example.com',
active: false,
}
]);
// useRef: 렌더링되지 않고도 관리하고 있는 변수 바로 조회 가능
// useRef( )의 파라미터에는 .current 값의 기본값이 된다.
const nextId = useRef(4);
// 유저 등록
const onCreate = () => {
// 리스트에 추가해줄 유저 정보 형식 id-이름-이메일
const user = {
id: nextId.current,
username,
email,
};
setUsers(users.concat(user)); // 새로운 항목(배열)을 만들어 뒤에 user 를 붙여준다.
// 함수가 호출되면 유저리스트에 추가(생성) 후 input 창을 비워준다.
setInputs({
username: '',
email: ''
});
nextId.current += 1;
};
// 유저 삭제
const onRemove = (id) => {
setUsers(users.filter(user => user.id !== id));
};
// 유저 토글(수정)
const onToggle = (id) => {
// 배열 내 특정 아이템만 업데이트할 때 map 을 사용할 수 있다.
setUsers(users.map(
user => user.id === id // 파라미터의 id 와 유저 id 가 같다면 기존의 user 를 가져와서 active 상태를 반전 시킨다.
? {...user, active: !user.active}
: user
));
};
return (
<>
{/* CreateUser에 넘겨줄 Props 값을 태그 내부에 다음과 같이 작성해준다. */}
{/* <CreateUser 넘겨줄props의이름=props값 /> */}
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList
users={users}
onRemove={onRemove}
onToggle={onToggle}
/>
</>
);
}
export default App;
UserList.js
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>
</div>
);
}
function UserList({ users, onRemove, onToggle }) {
return (
<div>
{users.map(user => (
<User
user={user}
key={user.id}
onRemove={onRemove}
onToggle={onToggle}
/>
))}
</div>
);
}
export default UserList;
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;