📌 배열 랜더링하기
🤔 배열 랜더링하기.
가령 아래와 같은 배열이 있을 때,
const users = [ { 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' } ];
이 내용을 컴포넌트로 랜더링 하고, 컴포넌트를 재사용 할 수 있게 한다면?
UserList.js import React from 'react'; function User({ user }) { return ( <div> <b>{user.username}</b> <span>({user.email})</span> </div> ); } function UserList() { const users = [ { 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' } ]; return ( <div> <User user={users[0]} /> <User user={users[1]} /> <User user={users[2]} /> </div> ); } export default UserList;
🤔 만약, 동적인 배열을 랜더링 해야한다면? → map() 을 사용!!
map() 함수는 배열안에 있는 각 원소를 변환하여 새로운 배열을 만든다.
react에서 동적인 배열을 랜더링해야 할 때는 이 함수를 사용하여 일반 데이터 배열을 react element로 이루어진 배열로 변환해주면 된다.
다만, react에서 배열을 랜더링할 때는 key라는 props를 설정해야 한다.
UserList.js import React from 'react'; function User({ user }) { return ( <div> <b>{user.username}</b> <span>({user.email})</span> </div> ); } function UserList() { const users = [ { 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' } ]; return ( <div> {users.map(user => ( <User user={user} key={user.id} /> ))} </div> ); } export default UserList;
만약 배열 안의 원소가 가지고 있는 고유한 값이 없다면 map() 함수를 사용 할 때 설정하는 콜백함수의 두번째 파라미터 index 를 key 로 사용하면 된다.Ex)
<div> {users.map((user, index) => ( <User user={user} key={index} /> ))} </div>
cf. useRef (https://chan4im.tistory.com/190)
🤔 useRef로 컴포넌트 안의 변수 만들기
useRef의 Hook은 2가지 용도가 있다.
1. DOM 선택 용도
2. 컴포넌트 안에서 조회 및 수정할 수 있는 변수를 관리하는 용도
관리를 위한 변수
∙ setTimeout, setInterval을 통해 만들어진 id
∙ 외부 라이브러리를 사용해 생성된 인스턴스
∙ scroll 위치
Ex) UserList에서 컴포넌트 내부에 배열을 직접선언하는 대신, App에서 배열을 선언, UserList에게 props로 전달하는 예제
UserList.js import React from 'react'; function User({ user }) { return ( <div> <b>{user.username}</b> <span>({user.email})</span> </div> ); } function UserList({ users }) { return ( <div> {users.map(user => ( <User user={user} key={user.id} /> ))} </div> ); } export default UserList;
App.js import React, { useRef } from 'react'; import UserList from './UserList'; function App() { const users = [ { 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' } ]; const nextId = useRef(4); const onCreate = () => { // 나중에 구현 할 배열에 항목 추가하는 로직 // ... nextId.current += 1; }; return <UserList users={users} />; } export default App;
App 에서 useRef() 를 사용하여 nextId 라는 변수를 생성하며,
useRef() 를 사용 할 때 파라미터를 넣어주면, 이 값이 .current 값의 기본값이 된다.
그리고 이 값을 수정 할때에는 .current 값을 수정하면 되고 조회 할 때에는 .current 를 조회하면 된다.
먼저 input 2개와 button 1개로 이루어진 CreateUser.js라는 컴포넌트를 src디렉토리에 추가
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;
🤔 배열에 항목 추가하기
위의 CreateUser에서 컴포넌트의 상태관리를 하지 않고
부모 컴포넌트인 App에서 상태관리를 진행, input값 및 이벤트 등록 함수들은 props로 넘겨받아 사용해보자.
이 컴포넌트를 App에서 UserList위에 랜더링 해보자.
App.js import React, { useRef } from 'react'; import UserList from './UserList'; import CreateUser from './CreateUser'; function App() { const users = [ { 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' } ]; const nextId = useRef(4); const onCreate = () => { // 나중에 구현 할 배열에 항목 추가하는 로직 // ... nextId.current += 1; }; return ( <> <CreateUser /> <UserList users={users} /> </> ); } export default App;
CreateUser 컴포넌트에게 필요한 props를 App에서 준비,
그 후, users에도 useState를 사용해 컴포넌트의 상태로서 관리.
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, [name]: value }); }; 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' } ]); const nextId = useRef(4); const onCreate = () => { // 나중에 구현 할 배열에 항목 추가하는 로직 // ... setInputs({ username: '', email: '' }); nextId.current += 1; }; return ( <> <CreateUser username={username} email={email} onChange={onChange} onCreate={onCreate} /> <UserList users={users} /> </> ); } export default App;
이제 배열에 변화를 줘야하는데, 이때 객체와 마찬가지로 불변성을 지켜줘야 한다.
따라서 배열의 push, splice, sort 등의 함수는 사용X
if, 사용해야할 때, 기존 배열을 한번 복사한 후 사용해야함.
📌 불변성을 지키면서 배열에 새 항목을 추가하는 방법
1. spread 연산자 사용
2. concat 함수 사용
- spread 연산자 사용(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, [name]: value }); }; 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' } ]); const nextId = useRef(4); const onCreate = () => { 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;
- concat 함수 사용(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, [name]: value }); }; 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' } ]); const nextId = useRef(4); const onCreate = () => { const user = { id: nextId.current, username, email }; setUsers(users.concat(user)); setInputs({ username: '', email: '' }); nextId.current += 1; }; return ( <> <CreateUser username={username} email={email} onChange={onChange} onCreate={onCreate} /> <UserList users={users} /> </> ); } export default App;
우선, UserList에 각 User 컴포넌트에 대해 삭제 버튼을 랜더링.
UserList.js
import React from 'react';
function User({ user, onRemove }) {
return (
<div>
<b>{user.username}</b> <span>({user.email})</span>
<button onClick={() => onRemove(user.id)}>삭제</button>
</div>
);
}
function UserList({ users, onRemove }) {
return (
<div>
{users.map(user => (
<User user={user} key={user.id} onRemove={onRemove} />
))}
</div>
);
}
export default UserList;
🤔 배열에 항목 제거하기
이때, User 컴포넌트의 삭제버튼이 클릭될 때, user.id값을 앞으로 props로 받아올 onRemove 함수의 파라미터로 넣어 호출해야
한다. (onRemove함수는 "id가 ~인 객체를 삭제하라"는 역할)
이때, onRemove함수는 UserList에서도 전달받고, 이를 그대로 User 컴포넌트에게 전달한다.
이제 onRemove를 구현해보자.
배열에 있는 항목제거 시, 추가할 때와 마찬가지로 불변성을 지켜가며 업데이트 해줘야 한다.
이때, filter라는 배열 내장 함수를 사용하는 것을 추천!!
App.jsimport 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, [name]: value }); }; 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' } ]); const nextId = useRef(4); const onCreate = () => { const user = { id: nextId.current, username, email }; setUsers(users.concat(user)); setInputs({ username: '', email: '' }); nextId.current += 1; }; const onRemove = id => { // user.id 가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듬 // = user.id 가 id 인 것을 제거함 setUsers(users.filter(user => user.id !== id)); }; return ( <> <CreateUser username={username} email={email} onChange={onChange} onCreate={onCreate} /> <UserList users={users} onRemove={onRemove} /> </> ); } export default App;
먼저 App 컴포넌트의 users 배열 안의 객체 안에 active라는 속성을 추가하자.
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,
[name]: value
});
};
const [users, setUsers] = useState([
{
id: 1,
username: 'velopert',
email: 'public.velopert@gmail.com',
active: true
},
{
id: 2,
username: 'tester',
email: 'tester@example.com',
active: false
},
{
id: 3,
username: 'liz',
email: 'liz@example.com',
active: false
}
]);
const nextId = useRef(4);
const onCreate = () => {
const user = {
id: nextId.current,
username,
email
};
setUsers(users.concat(user));
setInputs({
username: '',
email: ''
});
nextId.current += 1;
};
const onRemove = id => {
// user.id 가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듬
// = user.id 가 id 인 것을 제거함
setUsers(users.filter(user => user.id !== id));
};
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle} />
</>
);
}
export default App;
🤔 배열에 항목 수정하기
User 컴포넌트에 계정명 클릭 시, 색상이 초록색으로 바뀌고 다시 누르면 검정색으로 바뀌도록 하는 구현
이제, User 컴포넌트에서 active 값에 따라 폰트색이 바뀌도록 해보자.
cf. cursor 필드를 설정해 마우스를 올렸을 때, 손가락 모양으로 바뀌도록 하자.
UserList.js import React from 'react'; function User({ user, onRemove }) { return ( <div> <b style={{ cursor: 'pointer', color: user.active ? 'green' : 'black' }} > {user.username} </b> <span>({user.email})</span> <button onClick={() => onRemove(user.id)}>삭제</button> </div> ); } function UserList({ users, onRemove }) { return ( <div> {users.map(user => ( <User user={user} key={user.id} onRemove={onRemove} /> ))} </div> ); } export default UserList;
이제 App.js 에서 onToggle 이라는 함수를 구현,
배열의 불변성을 유지하면서 배열을 업데이트 할 때에도 map 함수를 사용 할 수 있다.id 값을 비교해서 id 가 다르다면 그대로 두고, 같다면 active 값을 반전시키도록 구현을 진행,
onToggle 함수를 만들어서 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 onChange = e => { const { name, value } = e.target; setInputs({ ...inputs, [name]: value }); }; const [users, setUsers] = useState([ { id: 1, username: 'velopert', email: 'public.velopert@gmail.com', active: true }, { id: 2, username: 'tester', email: 'tester@example.com', active: false }, { id: 3, username: 'liz', email: 'liz@example.com', active: false } ]); const nextId = useRef(4); const onCreate = () => { const user = { id: nextId.current, username, email }; setUsers(users.concat(user)); setInputs({ username: '', email: '' }); nextId.current += 1; }; const onRemove = id => { // user.id 가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듬 // = user.id 가 id 인 것을 제거함 setUsers(users.filter(user => user.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} /> <UserList users={users} onRemove={onRemove} onToggle={onToggle} /> </> ); } export default App;
그 다음에는 UserList 컴포넌트에서 onToggle를 받아와서 User 에게 전달해주고, onRemove 를 구현했었던것처럼 onToggle 에 id 를 넣어서 호출하자.
UserList.js import React from 'react'; function User({ user, onRemove, onToggle }) { return ( <div> <b style={{ cursor: 'pointer', color: user.active ? 'green' : 'black' }} onClick={() => onToggle(user.id)} > {user.username} </b> <span>({user.email})</span> <button onClick={() => onRemove(user.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;
'ETC(front,back) > React(HTML, CSS, JS)' 카테고리의 다른 글
<frontend> {15} </React_part 2. 동적 interaction> (0) | 2023.07.11 |
---|---|
<frontend> {14} </React_part 1. Basic> (0) | 2023.07.11 |
<frontend> {13} </JS와 객체, 문서객체모델(DOM)> (0) | 2023.07.05 |
<frontend> {12} </JS기본 문법, let과 const, onclick> (0) | 2023.07.05 |
<frontend> {11} </Javascript 기초용어 및 입출력 방법> (0) | 2023.07.05 |