[React] 컴포넌트 반복
react 스터디에서 리액트를 다루는 기술이라는 책을 선정했고 이 책을 읽고 배운 것을 바탕으로 작성되었다.
📌 배열 렌더링하기
Array.prototype.map(callback(currentValue, index, array), thisArg)
- 배열 각 요소에 대해 콜백함수를 실행하고 실행결과를 모은 새 배열을 리턴한다.
currentValue
: 현재 처리할 요소 값index
: 현재 요소의 인덱스 값array
: 메서드를 호출한 배열thisArg
: 콜백함수를 실행할 때 사용할this
function User({user}){
return(
<div>
<span>{user.username}</span>
<span>({user.email})</span>
</div>
);
}
function UserList(){
const users = [
{
id: 1,
username: 'velopert',
email: '[email protected]'
},
{
id: 2,
username: 'tester',
email: '[email protected]'
},
{
id: 3,
username: 'liz',
email: '[email protected]'
}
];
return (
<div>
users.map(user => <User user = {user} key = {user.id}/>);
</div>
);
}
export defalut UserList;
📙 key
💡 Key
는 React가 어떤 항목을 변경, 추가 또는 삭제할지 식별하는 것을 돕는다.
key 설정
key
값은 다른 항목들 사이에서 해당 항목을 고유하게 식별할 수 있는 값으로 설정해야 한다.- 만약 고유값이 없다면
index
값을key
값으로 사용하면 된다. key
는 배열 안 형제 사이에서 고유해야 하고 전체 범위에서 고유할 필요는 없다.
key 의 존재유무에 따른 업데이트 방식
const array = ['a', 'b', 'c', 'd'];
array.map(item => <div>{item}</div>);
- 위 배열의 b 와 c 사이에 z 를 삽입하게 된다면, 리렌더링을 하게 될 때
<div>b</div>
와<div>c</div>
사이에 새div
태그를 삽입을 하게 되는 것이 아니라, 기존의 c 가 z 로바뀌고, d 는 c 로 바뀌고, 맨 마지막에 d 가 새로 삽입된다. - 그 다음에 a 를 제거하게 된다면, 기존의 a 가 b 로 바뀌고, b 는 z 로 바뀌고, z는 c로 바뀌고, c는 d 로바뀌고, 맨 마지막에 있는 d 가 제거된다.
const array = [
{
id: 0,
text: 'a'
},
{
id: 1,
text: 'b'
},
{
id: 2,
text: 'c'
},
{
id: 3,
text: 'd'
}
];
array.map(item => <div key={item.id}>{item.text}</div>);
- 수정되지 않는 기존의 값은 그대로 두고 원하는 곳에 내용을 삽입하거나 삭제한다.
- 때문에, 배열을 렌더링 할 때에는 고유한
key
값이 있는 것이 중요하다. - 만약에 배열 안에 중복되는
key
가 있을 때에는 렌더링 시에 오류 메시지가 콘솔에 나타나게 되며, 업데이트가 제대로 이루어지지 않게 된다.
📌 배열에 항목 추가하기
- 배열에 항목을 추가하는 방법
spread
연산자 사용
concat
메서드 사용
- 기존의 배열을 수정하지 않고, 새로운 원소가 추가된 새로운 배열을 반환함.
- 컴포넌트는 내부의
state
나 props
가 변경될 때마다 re-rendering
되는데, 함수형 컴포넌트는 내부에 정의된 로컬 변수들을 초기화한다.
- 반면
useRef
로 만들어진 객체는 React가 만든 전역 저장소에 저장되기 때문에 리렌더링되더라도 마지막으로 업데이트한 current 값이 유지된다.
// UserList.js
function User({user}){
return(
<div>
<span>{user.username}</span>
<span>({user.email})</span>
</div>
);
}
function UserList({users}){
return (
<div>
users.map(user => <User user = {user} key = {user.id}/>);
</div>
);
}
export defalut UserList;
// CreateUser.js
function CreateUser({username, email, onChange, onCreate}){
return (
<div>
<input
type="text",
name="username",
value={username}
onChange={onChange}
placeholder="계정명"/>
<input
type="email"
name="email"
value={email}
onChange={onChange}
placeholder="이메일"/>
<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 [users, setUsers] = userState([
{
id: 1,
username: 'velopert',
email: '[email protected]'
},
{
id: 2,
username: 'tester',
email: '[email protected]'
},
{
id: 3,
username: 'liz',
email: '[email protected]'
}
]);
const nextId = useRef(4);
const onChange = (e) => {
const { name, value } = e.target;
setInputs({
...inputs,
[name]: value
});
};
const onCreate = () => {
const newUser = {
id: nextId.current;
username,
email
};
setUsers([...user, newUser ]); // spread 연산자 이용
// setUsers(user.concat(newUser)); // concat 메서드 이용
setInputs({
username: "",
email: ""
});
nextId.current++;
}
return (
<div>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} />
</div>
);
}
export default Createuser;
📌 배열에 항목 제거하기
spread
연산자 사용concat
메서드 사용- 기존의 배열을 수정하지 않고, 새로운 원소가 추가된 새로운 배열을 반환함.
state
나 props
가 변경될 때마다 re-rendering
되는데, 함수형 컴포넌트는 내부에 정의된 로컬 변수들을 초기화한다.useRef
로 만들어진 객체는 React가 만든 전역 저장소에 저장되기 때문에 리렌더링되더라도 마지막으로 업데이트한 current 값이 유지된다.// UserList.js
function User({user}){
return(
<div>
<span>{user.username}</span>
<span>({user.email})</span>
</div>
);
}
function UserList({users}){
return (
<div>
users.map(user => <User user = {user} key = {user.id}/>);
</div>
);
}
export defalut UserList;
// CreateUser.js
function CreateUser({username, email, onChange, onCreate}){
return (
<div>
<input
type="text",
name="username",
value={username}
onChange={onChange}
placeholder="계정명"/>
<input
type="email"
name="email"
value={email}
onChange={onChange}
placeholder="이메일"/>
<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 [users, setUsers] = userState([
{
id: 1,
username: 'velopert',
email: '[email protected]'
},
{
id: 2,
username: 'tester',
email: '[email protected]'
},
{
id: 3,
username: 'liz',
email: '[email protected]'
}
]);
const nextId = useRef(4);
const onChange = (e) => {
const { name, value } = e.target;
setInputs({
...inputs,
[name]: value
});
};
const onCreate = () => {
const newUser = {
id: nextId.current;
username,
email
};
setUsers([...user, newUser ]); // spread 연산자 이용
// setUsers(user.concat(newUser)); // concat 메서드 이용
setInputs({
username: "",
email: ""
});
nextId.current++;
}
return (
<div>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} />
</div>
);
}
export default Createuser;
Array.prototype.filter(callback(currentValue, index, array), thisArg)
- 각 배열 요소에 대해 콜백함수를 실행하고 실행 결과가 true인 요소들만 모은 새 배열을 반환한다.
currentValue
: 현재 처리할 요소 값index
: 현재 요소의 인덱스 값array
: 메서드를 호출한 배열thisArg
: 콜백함수를 실행할 때 사용할this
// UserList.js
function User({user, onRemove}){
return(
<div>
<span>{user.username}</span>
<span>({user.email})</span>
<button onClick={() => onRemove(user.id)}>삭제</button>
</div>
);
}
function UserList({users, onRemove}){
return (
<div>
users.map(user => <User user = {user} onRemove={onRemove} key = {user.id}/>);
</div>
);
}
export defalut UserList;
// 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 [users, setUsers] = userState([
{
id: 1,
username: 'velopert',
email: '[email protected]'
},
{
id: 2,
username: 'tester',
email: '[email protected]'
},
{
id: 3,
username: 'liz',
email: '[email protected]'
}
]);
const nextId = useRef(4);
const onChange = (e) => {
const { name, value } = e.target;
setInputs({
...inputs,
[name]: value
});
};
const onCreate = () => {
const newUser = {
id: nextId.current;
username,
email
};
setUsers([...users, newUser ]); // spread 연산자 이용
// setUsers(users.concat(newUser)); // concat 메서드 이용
setInputs({
username: "",
email: ""
});
nextId.current += 1;
}
const noRemove = (id) => {
setUsers(users.filter(user => user.id !== id));
}
return (
<div>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onRemove={onRemove}/>
</div>
);
}
export default Createuser;
onRemove
라는 이벤트 핸들러를 계속props
로 전달하여 최종적으로User
컴포넌트에 전달<button onClick={() => onRemove(user.id)}>삭제</button>
📌 배열에 항목 수정하기
User 컴포넌트에 계정명을 클릭했을때 색상이 초록색으로 바뀌고, 다시 누르면 검정색으로 바뀌도록 구현
// UserList.js
function User({user, onRemove, onToggle}){
return(
<div>
<span
onClick={() => onToggle(user.id)}
style = {{
cursor: "pointer",
color: user.active ? "green" : "black"
}}
>{user.username}</span>
<span>({user.email})</span>
<button onClick={() => onRemove(user.id)}>삭제</button>
</div>
);
}
function UserList({users, onRemove, onToggle}){
return (
<div>
users.map(user =>
<User
user = {user}
onRemove={onRemove}
onToggle={onToggle}
key={user.id}
/>
);
</div>
);
}
export defalut UserList;
// 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 [users, setUsers] = userState([
{
id: 1,
username: 'velopert',
email: '[email protected]',
active: true
},
{
id: 2,
username: 'tester',
email: '[email protected]',
active: false
},
{
id: 3,
username: 'liz',
email: '[email protected]',
active: false
}
]);
const nextId = useRef(4);
const onChange = (e) => {
const { name, value } = e.target;
setInputs({
...inputs,
[name]: value
});
};
const onCreate = () => {
const newUser = {
id: nextId.current;
username,
email
};
setUsers([...users, newUser ]); // spread 연산자 이용
// setUsers(users.concat(newUser)); // concat 메서드 이용
setInputs({
username: "",
email: ""
});
nextId.current += 1;
}
const onRemove = (id) => {
setUsers(users.filter(user => user.id !== id));
}
const onToggle = (id) => {
setUsers(users.map(user =>
user.id === id ? {...user, user.active: !user.active} : user
);
};
return (
<div>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle}/>
</div>
);
}
export default Createuser;
참고
Author And Source
이 문제에 관하여([React] 컴포넌트 반복), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@eunnbi/React-컴포넌트-반복저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)