일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 드래그앤드롭
- 다이나믹프로그래밍
- usedebugvalue
- 훅
- 알고리즘 #코딩테스트 #프로그래머스 #자바
- 프로그래머스 #알고리즘 #자바스크립트
- 완전탐색
- react-beautiful-dnd
- 덕타이핑
- 리액트
- useRef
- 자바스크립트
- 프로그래머스
- 위상정렬
- 타입스크립트
- javascript
- recoil
- useContext
- 알고리즘
- useLayoutEffect
- 스티커
- Suspense
- 리코일
- 알고리즘 #프로그래머스 #코딩테스트 #자바스크립트
- 백준
- 백준 #알고리즘 #자바스크립트
- React
- Today
- Total
몽환화
[React] useRef 물고뜯기 본문
useRef
렌더링에 필요하지 않은 값을 참조할 수 있는 React Hook
컴포넌트의 최상위 레벨에서 useRef를 호출하여 ref를 선언
const ref = useRef(initialValue)
컴포넌트가 일부 정보를 “기억”하고 싶지만, 해당 정보가 렌더링을 유발하지 않도록 하려면 ref를 사용하세요.
매개변수 initialValue : ref 객체의 current 프로퍼티 초기 설정값, 초기 렌더링 이후부터는 무시됨
반환값 ref : 단일 프로퍼티를 가진 다음과 같은 객체를 반환한다
{
current: 0 // useRef에 전달한 값
}
current : 처음에는 전달한 initialValue로 설정된다
ref.current 프로퍼티를 통해 해당 ref의 current 값에 접근할 수 있다.
=> 의도적으로 변경할 수 있으므로 읽고 쓸 수 있다. 리액트가 추적하지 않는다
- ref.current 프로퍼티는 state와 단리 변이할 수 있다. 그러나 렌더링에 사용되는 객체를 포함하는 경우 해당 객체를 변이해서는 안된다
- ref.current 프로퍼티를 변경해도 컴포넌트를 다시 렌더링하지 않는다. ref는 일반 자바스크립트 객체이다! 리액트사용자가 언제 변경했는지 모른다.
- 초기화를 제외하고는 렌더링 중에 ref.current를 쓰거나 읽지 말아야 한다!
그러니까 컴포넌트가 렌더링 될 때 생성이 되지만 얘가 변경된다고 해서 리렌더링이 발생되지는 않는게 useRef
state와 마찬가지로 ref도 리액트의 리렌더링에 의해 유지되지만 state는 설정되면 컴포넌트가 다시 렌더링되고, ref를 변경하면 다시 렌더링되지 않는다.
useRef는 왜 필요한가,,,
그냥 함수 외부에서 값을 선언해서 관리하면 안되나?
=>
- 렌더링 되기 전에도 기본적으로 해당 값을 지니기 때문에 메모리에 불필요한 내용이 들어가게 된다.
- 컴포넌트가 여러번 생성된다면 각 컴포넌트에서 가리키는 값이 모두 동일해진다.
- 대부분의 경우 컴포넌트 인스턴스 하나당 하나의 값을 필요로 한다.
import { useRef } from 'react';
function MyComponent() {
const intervalRef = useRef(0);
const inputRef = useRef(null);
// ...
ref는 변경해도 리렌더링을 촉발하지 않기 때문에 시각적 출력에 영향을 미치지 않는 정보를 저장하는 데 적합하다
ref 내부의 값을 업데이트하려면 current 프로퍼티를 수동으로 변경해야 한다
화면에 표시되는 정보를 저장하는데는 ref보다는 state가 적합하다
import { useRef } from "react";
export default function RefCounter() {
let ref = useRef(0);
function handleClick() {
ref.current = ref.current + 1;
alert("You clicked " + ref.current + " times!");
}
return (
<div>
<button onClick={handleClick}>Click me!</button>
<div>{ref.current}</div>
</div>
);
}
버튼을 클릭하면 You clicked * times! 에서 수가 계속 증가한다 => 그 시점의 ref.current이다
그러나 화면상의 {ref.current} 는 초기 렌더링 당시의 값이므로 0을 나타내며, 버튼을 누른다고 해도 리렌더링이 되지 않아 계속 0인 상태를 유지한다
그러니까
ref.current의 값이 변하지 않는다는게 아니다
쟤도 계속 변하지 우리가 버튼을 클릭할 때 마다 alert에 뜨는 값은 변화한다 그때의 ref.current를 띄워주니까
그런데 리렌더링이 되는게 아니니까 그냥 화면에는 초기값이 그대로 있는거야
화면은 그냥 변화가 없는거
ref로 DOM 조작하기
초기값이 null인 ref 객체를 생성한다
const ref = useRef(null)
(선언을 했어도 렌더링 하기 전이면 ref.current를 찍었을 때 undefined가 나온다!)
ref객체를 ref속성으로 조작하려는 DOM 노드의 JSX에 전달
return <input ref={inputRef} />
리액트가 DOM 노드를 생성하고 화면에 그린 후 리액트는 ref 객체의 current 프로퍼티를 DOM 노드로 설정함
focus() 같은 메서드 호출 가능!
function handleClick() {
inputRef.current.focus();
}
노드가 화면에서 제거되면 리액트는 current 프로퍼티를 다시 null로 설정함
커스텀 컴포넌트에선 ref를 보낼 수 없다
const inputRef = useRef(null);
return <MyInput ref={inputRef} />;
=> 에러 발생!
이때는 저 MyInput 컴포넌트를
import { forwardRef } from 'react';
const MyInput = forwardRef(({ value, onChange }, ref) => {
return (
<input
value={value}
onChange={onChange}
ref={ref}
/>
);
});
export default MyInput;
이렇게 forwartRef로 감싸면 해결된다
리액트의 모든 갱신은 다음과 같은 두 단계이다
- 렌더링 : 리액트 화면에 무엇을 그려야하는지 알아내도록 컴포넌트를 호출한다
- 커밋 : 리액트는 변경사항을 DOM에 적용한다
일반적으로 렌더링하는 중에 ref에 접근하는 것을 원하지 않는다. 첫 렌더링에서는 DOM노드가 아직 생성되지 않아 ref.current는 null 인 상태!
리액트는 ref.current를 커밋 단계에서 설정을 한다.
대부분의 ref에 대한 접근은 이벤트 핸들러에서 일어난다.
useState와 동일하게 컴포넌트 내부에서 렌더링이 일어나도 변경 가능한 상태값을 저장한다는 공통점이 있다
그러나
- useRef는 반환값인 객체 내부에 있는 currendt로 값에 접근 또는 변경할 수 있다
- useRef는 그 값이 변하더라도 렌더링을 발생시키지 않는다
=> useRef로 useState를 흉내내어도 렌더링은 발생하지 않는다
useRef는 DOM 뿐만 아니라 어떤 값이든 저장할 수 있는 일반적인 자바스크립트 객체이다.
일반적인 프로그래밍 언어는 heap 영역과 stack 영역에서 메모리를 관리한다
stack 공간은 함수가 실행될 때 메모리에 할당됐다가 종료되면서 한꺼번에 해제된다
반면 heap은 전역변수와 참조타입의 변수를 할당하고 가비지 컬렉터를 이용해 사용하지 않는 메모리를 해제시킨다.
즉 우리가 자바스크립트 객체로 만드는 변수들은 모두 heap 공간에 할당되었다가 해제된다
useRef()는 일반적인 자바스크립트 객체로 heap영역에 저장되는 변수이다
heap영역에 저장되어있기 때문에 어플리케이션이 종료되거나 가비지 컬렉팅될 때 까지 참조할때마다 같은 메모리 값을 가진다고 할 수 있다
값이 변경되어도 리렌더링이 되지 않는다
같은 메모리 주소를 갖고있기 때문에 자바스크립트의 ===연산이 항상 true를 반환하여 변경사항을 감지하지 못한다.
렌더링을 발생시키지 않고 원하는 상태값을 저장할 수 있다는 특징 활용 => useState의 이전 값을 저장한다
그렇다면 state가 업데이트 되어서 리렌더링이 발생했을 때에 useRef의 값은 유지되는가?
=> 유지된다.
ref | state |
useRef(initialValue) 는 { current: initialValue } 을 반환 | useState(initialValue) 은 state 변수의 현재 값과 setter 함수 [value, setValue] 를 반환 |
state를 바꿔도 리렌더 되지 않는다 | state를 바꾸면 리렌더 된다 |
Mutable-렌더링 프로세스 외부에서 current 값을 수정 및 업데이트할 수 있음 | ”Immutable”—state 를 수정하기 위해서는 state 설정 함수를 반드시 사용하여 리렌더 대기열에 넣어야 함 |
렌더링 중에는 current 값을 읽거나 쓰면 안 된다 | 언제든지 state를 읽을 수 있음. 그러나 각 렌더마다 변경되지 않는 자체적인 state의 snapshot이 있음 |
참고자료
도서 :: 모던 리액트 Deep Dive https://product.kyobobook.co.kr/detail/S000210725203
https://ko.react.dev/learn/escape-hatches
'Front > React' 카테고리의 다른 글
[React] Recoil 뜯어보기 (0) | 2024.05.22 |
---|---|
[React] useLayoutEffect, useDebugValue (0) | 2024.05.06 |
[React] useImperativeHandle 물고뜯기 (0) | 2024.05.06 |
[React] useReducer 물고뜯기 (0) | 2024.05.03 |
[React] useContext 물고뜯기 (0) | 2024.05.02 |