10. useRef 로 특정 DOM 선택
- ref, useRef 의미
- ref는 React에서 DOM 요소나 클래스 인스턴스를 참조할 수 있게 해주는 속성이며 주로 직접 DOM 접근이 필요한 경우 사용
- useRef는 함수형 컴포넌트에서 ref를 사용할 때 사용하는 React Hook.
- useRef는 렌더링 간에 값을 유지할 수 있도록 함
- ref와 함께 DOM 요소나 값을 참조하는 데 사용됨
- react에서 ref를 사용하는이유
- DOM 접근: React의 가상 DOM은 실제 DOM을 추상화하기 때문에, 직접 DOM을 조작해야 할 때 ref를 사용함. (ex 포커스를 설정하거나, 스크롤 위치를 조절할 때 )
- 자식 컴포넌트의 접근: 클래스 기반 컴포넌트에서 자식 컴포넌트의 메서드나 인스턴스에 접근해야 할 때 ref를 사용함. 함수형 컴포넌트에서는 useRef를 통해 접근할 수 있음
- 비동기 작업 처리: 특정 DOM 요소에서 비동기 작업(예: AJAX 호출, 타이머 등)을 실행할 때 ref가 필요할 수 있다.
useRef
- javascript에서 특정 Dom을 선택하는 역할 ex) getElementById, querySelector
- 특정 DOM에 접근할 때 사용한다.
- 외부 라이브러리 사용할때 유용하다.
- 원하는 위치에 ref={} 의 형태로 작성하면 된다.
- 포커스를 잡으려면 nameInput.current.focus() 형태로 작성하면 된다.
DOM이란 예를 들어, HTML 문서에서 <div>, <input>, <button> 같은 태그 각각이 DOM 요소임.
React에서는 직접 DOM 요소를 조작하지 않고 상태(state)와 props를 통해 UI를 업데이트하는 것을 권장함. 그러나 특정 상황에서는 직접 DOM 요소에 접근해야 할 때가 있는데 이때 ref를 사용하여 DOM 요소를 참조한다!
import React, { useState, useRef } from "react";
function InputSample() {
// 상태 변수 inputs를 초기화, setInputs는 inputs을 제어하는 setter함수,
// inputs 객체에서 name과 nickname을 빈 문자열로 설정
const [inputs, setInputs] = useState({
name: "",
nickname: ""
});
// nameInput에 ref를 설정하여 DOM 요소에 접근할 수 있게 함
const nameInput = useRef();
// 비구조화 할당을 통해 inputs 객체의 name과 nickname 값을 추출
const { name, nickname } = inputs;
// input 값이 변경될 때 호출되는 함수
const onChange = (e) => {
// e.target에서 name과 value 값을 추출
const { value, name } = e.target;
// setInputs 함수를 통해 inputs 상태를 업데이트
setInputs({
...inputs, // 기존 inputs 객체를 복사한 뒤
[name]: value // name 키를 가진 값을 새로운 value로 설정
});
};
// 초기화 버튼이 클릭될 때 호출되는 함수
const onReset = () => {
// inputs 상태를 초기값으로 설정
setInputs({
name: "",
nickname: ""
});
// nameInput에 포커스를 설정
nameInput.current.focus();
};
return (
<>
<input
name="name" // input 요소의 name 속성을 'name'으로 설정
placeholder="이름" // input 요소의 placeholder를 '이름'으로 설정
onChange={onChange} // 값이 변경될 때 onChange 함수 호출
value={name} // input 요소의 값을 상태 변수 name으로 설정
ref={nameInput} // ref를 설정하여 nameInput에 접근할 수 있게 함
/>
<input
name="nickname" // input 요소의 name 속성을 'nickname'으로 설정
placeholder="닉네임" // input 요소의 placeholder를 '닉네임'으로 설정
onChange={onChange} // 값이 변경될 때 onChange 함수 호출
value={nickname} // input 요소의 값을 상태 변수 nickname으로 설정
/>
<button onClick={onReset}>초기화</button>
{" "}
{/*버튼 클릭 시 onReset 함수 호출*/}
<div>
<b>값: </b>
{name} ({nickname}) {/*현재 name과 nickname 값을 화면에 출력*/}
</div>
</>
);
}
export default InputSample;
Q. 왜 nickname에서는 ref설정 안하는지?? 그리고 ref를 왜, 어떤 기준으로 설정하는지 정확하게는 모르겠음.
A. name에서 ref의 속성 중 focus 설정을 해놓았음. (nameInput.current.focus(); )
이때 focus 설정이란 웹 페이지나 애플리케이션에서 특정 입력 요소(예: 텍스트 입력 필드, 버튼 등)를 활성화하여 사용자가 바로 입력할 수 있도록 하는 것을 의미함.
>>focus 설정의 예시<<
- 로그인 폼 예시:
- 웹사이트에 로그인 폼이 있음
- 사용자가 페이지를 로드하면, 커서가 자동으로 "사용자 이름" 입력 필드에 위치함
- 사용자는 키보드를 사용하여 바로 사용자 이름을 입력할 수 있음
- 검색 창 예시:
- 웹사이트에 검색 창이 있음
- 페이지가 로드되면, 검색 입력 필드가 선택됨
- 사용자는 마우스를 사용하지 않고도 키보드로 검색어를 입력할 수 있음
11장 배열 랜더링
리액트에서 배열을 렌더링 한번에 map할때는 key 라는 props 를 설정해야함!
key 값은 각 원소들마다 가지고 있는 고유값으로 설정을 해야 함. 지금의 경우엔 id 가 고유 값임
Q. 여기 user가 너무많은데 각 user이 각각 어떤 것이고 무슨 역할을 하는지 잘 모르겠다.
return (
<div>
{users.map(user => ( //users의 배열에서 순회하며(map) 객체(3개)를 user(변수)라고 선언.
<User user={user} key={user.id} /> //User컴포의 props인 각 user 객체를 User 컴포넌트로 렌더링하고
컴포에서 고유한 key 속성을 설정
))}
</div>
);
A.
과정
- map 함수는 배열의 첫 번째 요소 { id: 1, username: 'velopert', email: 'public.velopert@gmail.com' }를 user 변수에 할당하고 콜백을 실행한 후, 두 번째 요소, 세 번째 요소로 넘어감
- user 변수는 users 배열의 각 요소를 순차적으로 가리킴
결론
- users.map(user => (...))의 각 user는 users 배열의 서로 다른 요소를 가리킴
- user 변수는 User 컴포넌트에 props로 전달되어 해당 사용자의 정보를 렌더링하는 역할을 함
- key prop은 각 컴포넌트를 고유하게 식별하여 React가 효율적으로 컴포넌트를 업데이트할 수 있도록 도와줍니다. (컴포에서 실행되지 않아도 key props로 인해 효율적 업데이트 가능!!)
만약 배열 안의 원소가 가지고 있는 고유한 값이 없다면 map() 함수를 사용 할 때 설정하는 콜백함수의 두번째 파라미터 index 를 key 로 사용
<div>
{users.map((user, index) => (
<User user={user} key={index} />
))}
</div>
12장 useRef 객체 유지
useRef는 React 훅으로, 다음과 같은 경우에 사용:
- DOM 요소에 접근: 특정 DOM 요소에 직접 접근하고 조작해야 할 때 사용됨
- Mutable 객체 유지: 컴포넌트가 다시 렌더링될 때도 유지되는 변경 가능한 객체를 만들기 위해 사용됨 (useRef는 current 속성을 가지며, 이 속성은 변경해도 컴포넌트가 다시 렌더링되지 않음)
- const nextId = useRef(4);:
- nextId는 새로운 사용자의 ID를 추적
- useRef(4)를 사용하여 초기값을 4로 설정합니다. 이 값은 컴포넌트가 다시 렌더링되어도 유지
- const onCreate = () => { nextId.current += 1; };:
- onCreate 함수는 새로운 사용자가 생성될 때 호출됨
- nextId.current 값을 1 증가시켜 다음 사용자에게 새로운 고유 ID를 부여할 준비
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"
}
];
// 다음에 생성될 사용자의 ID를 추적하는 ref를 초기값 4로 생성
const nextId = useRef(4);
// 새로운 사용자를 생성할 때 호출되는 함수
const onCreate = () => {
// nextId.current 값을 1 증가시켜 다음 사용자 ID 준비
nextId.current += 1;
};
// **UserList 컴포넌트에 users 배열을 prop으로 전달**
return <UserList users={users} />;
}
export default App;
—> 결론적으로 이와 같이 useRef를 사용하면 컴포넌트가 다시 렌더링되더라도 nextId 값을 유지할 수 있으며, 이를 통해 고유한 ID를 부여할 수 있습니다
13장 항목에 배열 추가
CreateUser.js : App컴포의 props를 넘겨받아서 리턴값만 있음
import React from 'react';
const 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;
UserList.js : users배열들을 순회하며 순차적으로 map을 쓰고 받은 User와 UseList함수로 기록되는 값을 가짐.
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";
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;
—> 버튼을 눌렀을 때 : input값들 초기화 안되며 동시에 밑에 배열이 추가가 안되는 상태임.
따라서
1. input 값들을 제어해줄 로직 필요(onChange)
const [inputs, setInputs] = useState({
username: "",
email: "",
});
const { username, email } = inputs;
const onChange = e => {
// e.target에서 name과 value를 추출
const { name, value } = e.target;
// 기존의 inputs 객체를 복사한 뒤,
// 특정 name 키를 가진 값을 변경된 value로 설정
setInputs({
...inputs, // 기존의 inputs 객체를 복사
[name]: value // name 키를 가진 값을 value로 설정
});
};
2. 배열 추가, 초기화 되는 로직 필요 (onCreate)
const onCreate = () => {
const user = {
id: nextId.current, // nextId.current 값을 id로 사용
username, // 현재 입력된 username 값
email, // 현재 입력된 email 값
};
// 새로운 사용자 객체(user)를 기존 사용자 목록(users)에 추가
setUsers(users.concat(user));
// 입력 필드를 초기화 (username과 email을 빈 문자열로 설정)
setInputs({
username: "",
email: "",
});
// nextId.current 값을 1 증가시켜 다음 사용자 ID 준비
nextId.current += 1;
};
배열에 항목 추가
- setUsers(users.concat(user)); concat (배열 내장 함수) 사용
- setUsers([...users, user]); spread 사용
14장 배열에 항목 제거
UserList.js : 각 User 컴포넌트를 보여줄 때, 삭제 버튼을 렌더링
import React from 'react';
function User({ user, onRemove }) {
return (
<div>
<b>{user.username}</b> <span>({user.email})</span>
<button onClick={() => onRemove(user.id)}>삭제</button>
// 삭제 버튼이 클릭 될 때는 user.id 값을 앞으로 props 로 받아올 onRemove 함수의 파라미터로 넣어서 호출해주어야 함
// onRemove는 "id 가 __인 객체를 삭제해라"인 역할
</div>
);
}
function UserList({ users, onRemove }) {
return (
<div>
{users.map(user => (
<User user={user} key={user.id} onRemove={onRemove} />
//User컴포에게 전달
))}
</div>
);
}
export default UserList;
App.js : onRemove 함수를 구현
기존 App.js에서 onRemove 함수 생성만 하면 됨
const onRemove = id => {
// user.id 가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듬
// = user.id 가 id 인 것을 제거함
setUsers(users.filter(user => user.id !== id));
};
배열에 항목 제거
- filter (배열 내장 함수) 사용.
- 이 함수는 배열에서 특정 조건이 만족하는 원소들만 추출하여 새로운 배열을 만들어줌
15장 배열 항목 수정
계정명을 클릭했을때 색상이 초록색으로 바뀌고, 다시 누르면 검정색으로 바뀌도록 구현
- App 컴포
- users 배열 안의 객체 안에 active 라는 속성을 추가
- UserList컴포
- active 값에 따라 폰트의 색상을 바꿔주도록 구현
- cursor 필드를 설정하여 마우스를 올렸을때 커서가 손가락 모양으로 변하도록 구현
- App컴포에서 onToggle 함수 구현
- map 사용(배열의 불변성을 유지하면서 배열을 업데이트)
- UserList 컴포
- onToggle를 받아와서 User 에게 전달
- onRemove 를 구현했었던것처럼 onToggle 에 id 를 넣어서 호출
App.js (기존 App에서 추가된 것만)
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 onToggle = (id) => {
setUsers( // setUsers 함수를 호출하여 users 상태를 업데이트
users.map((user) =>
user.id === id ? { ...user, active: !user.active } : user)
// 만약 현재 순회 중인 user 객체의 id가 매개변수로 전달받은 id와 같으면
// user 객체를 복사한 뒤 active 속성을 반전시킨 새로운 객체로 대체
// id가 일치하지 않는 경우 기존 user 객체를 그대로 반환
);
};
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle} />
</>
);
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)}
> //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;
'WINK-(Web & App) > React.js 스터디' 카테고리의 다른 글
[2024 여름방학 React.js 스터디] 이종윤 #5주차 (1) | 2024.08.08 |
---|---|
[2024 여름방학 React.js 스터디] 이정욱 #4주차 react 하기 힘들어요 (0) | 2024.08.02 |
[2024 여름방학 React.js 스터디] 이종윤 #4주차 (0) | 2024.08.02 |
[2024 여름방학 React.js 스터디] 강보경 #4주차 (0) | 2024.08.02 |
[2024 여름방학 React.js 스터디] 김태일 #4주차 "리액트 입문(2)" (1) | 2024.08.02 |