일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- [파이썬 실습] 중급 문제
- 엘리스 AI 트랙 5기
- 프론트개발공부
- JavaScript
- HTML
- 개발공부
- 코드스테이츠
- [파이썬 실습] 심화 문제
- RN 프로젝트
- reactnativecli
- 엘리스 ai 트랙
- 엘리스
- 자바스크립트 reduce()
- 삼항연산자
- 프론트개발
- 간단한 날씨 웹 만들기
- 개발일기
- 프로그래머스
- 리트코드
- 자바스크립트 split()
- leetcode
- 코딩부트캠프
- 자바스크립트
- [파이썬 실습] 기초 문제
- 자바스크립트 날씨
- [AI 5기] 연습 문제집
- 자바스크립트 날씨 웹 만들기
- 부트캠프
- 날씨 웹 만들기
- 자바스크립트 sort()
- Today
- Total
개발조각
[React] useRef_변수 관리 본문
useRef로 변수관리 (개념 설명)
const ref = useRef(value);
함수형 컴포넌트에서 useRef를 부르면 ref오브젝트를 반화해 준다.
ref오브젝트는 아래와 같이 생겼다.
{current: value}
인자로 넣어준 초기값은 Ref안에 있는 current에 저장이 된다.
{current: "hi"} => const ref = useRef("hi");
{current: "hello"} => const ref = useRef("hello");
{current: "nice"} => const ref = useRef("nice");
ref오브젝트는 수정이 가능하기 때문에 언제든지 원하는 값으로 바꿀 수 있으며,
반환된 ref는 컴포넌트의 전 생애주기를 통해 유지가 된다.
=> 컴포넌트가 계속 렌더링 되어도 컴포넌트가 언마운트가 되기 전까지는 값을 그대로 유지할 수 있다.
그럼 useRef는 언제 사용이 될까?
1. 저장공간
ref는 state와 비슷하게 어떠한 값을 저장하는 공간으로 사용이 된다.
State의 변화 → 렌더링 → 컴포넌트 내부 변수들 초기화
state가 변경이 되면 자동으로 컴포넌트가 다시 렌더링이 된다.
함수형 컴포넌트는 말 그대로 함수이기 때문에 리렌더링이 되면 함수가 다시 불려지고 내부에 있는 모든 변수들이 초기화가 된다.
그래서 가끔 원하지 않는 렌더링 값 때문에 곤란해질 경우가 발생하게 된다.
그럼 state대신 ref에 값을 저장하면 어떤 장점이 있을까?
Ref의 변화 → No 렌더링 → 변수들의 값이 유지됨
ref의 유용한 점은 ref안에 있는 값을 아무리 변경해도 컴포넌트는 다시 렌더링이 되지 않는다는 점이다.
그러므로 state대신 ref를 사용하면 불필요한 렌더링을 막을 수 있다.
State의 변화 → 렌더링 → 그래도 Ref의 값은 유지됨
또한 컴포넌트가 아무리 렌더링이 되어도 ref에 저장된 값은 변화되지 않고 그대로 유지되기 때문에
변경시 발생시키지 말아야 되는 값을 다룰 때 편리하다.
2. DOM 요소에 접근
대표적으로 input요소에 클릭하지 않아도 focus를 주고 싶을 때 많이 사용된다.
Document.querySelector()와 유사하다.
예제 1 (state와 ref의 차이)
(잊지 말자) 리액트에서 state가 변경된다는 것은 컴포넌트가 렌더링 된다는 것을 의미한다.
함수형 컴포넌트이기 때문에 렌더링 된다는 것은 함수(UseRef1)를 다시 불리게 된다.
import { useState, useEffect, useRef } from "react";
function App() {
return (
<>
<UseRef1 />
</>
);
}
const UseRef1 = () => {
const [count, setCount] = useState(0);
const countRef = useRef(0);
console.log(countRef);
console.log("🎨 랜더링");
const increaseCountState = () => {
setCount(count + 1);
};
return (
<div>
<p>State: {count}</p>
<button onClick={increaseCountState}>State 올려</button>
</div>
);
};
export default App;
이와 같이 ref는 하나의 오브젝트이며 ref안에는 current를 가지고 있고, current안에는 초기값 0을 가지고 있다. => {current: 0}
ref안에 있는 값을 접근하고 싶으면 아래와 같이 접근해 주면 된다.
countRef.current
countRef.current 추가
import { useState, useEffect, useRef } from "react";
function App() {
return (
<>
<UseRef1 />
</>
);
}
const UseRef1 = () => {
const [count, setCount] = useState(0);
const countRef = useRef(0);
console.log(countRef);
console.log("🎨 랜더링");
const increaseCountState = () => {
setCount(count + 1);
};
return (
<div>
<p>State: {count}</p>
<p>Ref: {countRef.current}</p>
<button onClick={increaseCountState}>State 올려</button>
</div>
);
};
export default App;
state, ref 둘 다 0을 가리킨다.
Ref 올려 클릭 시 카운트 증가 기능 추가
import { useState, useEffect, useRef } from "react";
function App() {
return (
<>
<UseRef1 />
</>
);
}
const UseRef1 = () => {
const [count, setCount] = useState(0);
const countRef = useRef(0);
console.log(countRef);
console.log("🎨 랜더링");
const increaseCountState = () => {
setCount(count + 1);
};
const increaseCountRef = () => {
countRef.current = countRef.current + 1;
};
return (
<div>
<p>State: {count}</p>
<p>Ref: {countRef.current}</p>
<button onClick={increaseCountState}>State 올려</button>
<button onClick={increaseCountRef}>Ref 올려</button>
</div>
);
};
export default App;
State 올려를 클릭하면 state의 카운트가 증가되는 것을 볼 수 있다.
콘솔로그가 계속해서 찍히는 이유는 state가 증가될 때마다 컴포넌트가 다시 렌더링이 되기 때문이다.
그러나 Ref 올려를 클릭하면 아무 일도 안 일어난다.
=> 그렇다고 진짜로 아무런 일이 안 일어나는 것은 아니다!!
ref는 아무리 수정을 해도 컴포넌트를 다시 렌더링 시키지 않는다.
컴포넌트 안에서 ref가 계속 증가하고 있는 것은 맞지만 렌더링이 되지 않기 때문에 아무리 증가해도 화면에 반영이 되지 않는다.
여기서 State 올려를 클릭하면 다시 렌더링이 일어나고 화면이 업데이트가 되기 때문에 Ref값도 업데이트가 된다.
Ref 올려를 클릭했을 때 진짜로 카운트가 증가하는지 확인
import { useState, useEffect, useRef } from "react";
function App() {
return (
<>
<UseRef1 />
</>
);
}
const UseRef1 = () => {
const [count, setCount] = useState(0);
const countRef = useRef(0);
console.log(countRef);
console.log("🎨 랜더링");
const increaseCountState = () => {
setCount(count + 1);
};
const increaseCountRef = () => {
countRef.current = countRef.current + 1;
console.log("Ref: ", countRef.current);
};
return (
<div>
<p>State: {count}</p>
<p>Ref: {countRef.current}</p>
<button onClick={increaseCountState}>State 올려</button>
<button onClick={increaseCountRef}>Ref 올려</button>
</div>
);
};
export default App;
Ref 올려를 클릭하면 콘솔에는 증가하는 게 보이지만 화면이 렌더링이 되지 않기 때문에 화면에서는 보이지 않는다.
여기서 State 올려를 클릭하면 화면 렌더링이 되기 때문에 state도 증가하고 ref도 한 번에 증가하는 걸 볼 수 있다.
여기서 Ref의 장점은?
자주 바뀌는 값을 state에 넣었다고 가정하면 그 값이 바뀔 때마다 화면이 계속 렌더링이 된다.
이렇게 되면 성능에 안 좋은 영향을 미치게 된다.
그래서 state가 아닌 ref를 사용하게 되면 아무리 바뀌어도 렌더링을 발생시키지 않기 때문에 성능에 좋다.
예제 2(컴포넌트 내부의 그냥 변수와 ref의 차이)
import { useState, useEffect, useRef } from "react";
function App() {
return (
<>
<UseRef2 />
</>
);
}
const UseRef2 = () => {
const countRef = useRef(0);
let countVar = 0;
const increaseCountRef = () => {
countRef.current = countRef.current + 1;
console.log("Ref: ", countRef.current);
};
const increaseCountVar = () => {
countVar += 1;
console.log("Var: ", countVar);
};
return (
<div>
<p>Ref: {countRef.current}</p>
<p>Var: {countVar}</p>
<button onClick={increaseCountRef}>Ref 올려</button>
<button onClick={increaseCountVar}>Var 올려</button>
</div>
);
};
export default App;
ref의 변화, 변수의 변화 둘 다 렌더링을 발생시키지 않음으로 화면 업데이트가 되지 않는다.
화면을 업데이트시켜줄 state 추가
import { useState, useEffect, useRef } from "react";
function App() {
return (
<>
<UseRef2 />
</>
);
}
const UseRef2 = () => {
const [renderer, setRenderer] = useState(0);
const countRef = useRef(0);
let countVar = 0;
const doRendering = () => {
setRenderer(renderer + 1);
};
const increaseCountRef = () => {
countRef.current = countRef.current + 1;
console.log("Ref: ", countRef.current);
};
const increaseCountVar = () => {
countVar += 1;
console.log("Var: ", countVar);
};
return (
<div>
<p>Ref: {countRef.current}</p>
<p>Var: {countVar}</p>
<button onClick={doRendering}>렌더!</button>
<button onClick={increaseCountRef}>Ref 올려</button>
<button onClick={increaseCountVar}>Var 올려</button>
</div>
);
};
export default App;
- Ref 올려 3번 클릭
- Var 올려 2번 클릭
- 렌더 클릭
했더니 Ref는 변했는데 변수는 변하지 않았다.
이유는 컴포넌트가 렌더링 된다는 것은 컴포넌트를 나타내는 함수가 다시 불리기 때문이고,
함수가 다시 불린다는 것은 변수들이 다시 초기화가 된다는 것을 의미한다.
=> 함수가 불릴 때마다 countVar변수는 0으로 초기화가 되는 것이다.
하지만 ref는 컴포넌트가 렌더링이 된데도 계속 값을 유지한다.
왜냐하면 ref의 값은 컴포넌트 전 생애주기를 통해 유지가 된다.
이 말은 컴포넌트가 브라우저에 마운팅 된 시점부터 마운트 해제 될 때까지 같은 값을 계속해서 유지할 수 있다.
그래서 ref는 클릭할 때마다 1씩 증가하는 것을 볼 수 있지만
var은 1씩 증가하다가 렌더가 되면 다시 0부터 시작하는 것을 알 수 있다.
다시 확인하기기
import { useState, useEffect, useRef } from "react";
function App() {
return (
<>
<UseRef2 />
</>
);
}
const UseRef2 = () => {
const [renderer, setRenderer] = useState(0);
const countRef = useRef(0);
let countVar = 0;
const doRendering = () => {
setRenderer(renderer + 1);
};
const increaseCountRef = () => {
countRef.current = countRef.current + 1;
console.log("Ref: ", countRef.current);
};
const increaseCountVar = () => {
countVar += 1;
console.log("Var: ", countVar);
};
const printResults = () => {
console.log(`ref: ${countRef.current}, var: ${countVar}`);
};
return (
<div>
<p>Ref: {countRef.current}</p>
<p>Var: {countVar}</p>
<button onClick={doRendering}>렌더!</button>
<button onClick={increaseCountRef}>Ref 올려</button>
<button onClick={increaseCountVar}>Var 올려</button>
<button onClick={printResults}>Ref Var 값 출력</button>
</div>
);
};
export default App;
- Ref 올려 3번 클릭
- Var 올려 2번 클릭
- Ref Var 값 출력 클릭
- 렌더! 클릭
- Ref Var 값 출력 클릭 -> 변수는 0으로 초기화됨
예제 3 (Ref가 유용한 상황)
몇 번 렌더링이 되었는지 확인
(useEffect의 의존성 배열이 없음 -> 렌더링 될 때마다 useEffect가 호출된다.)
import { useState, useEffect, useRef } from "react";
function App() {
return (
<>
<UseRef3 />
</>
);
}
const UseRef3 = () => {
const [count, setCount] = useState(1);
const [renderCount, setRenderCount] = useState(1);
useEffect(() => {
console.log("렌더링!");
setRenderCount(renderCount + 1);
});
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>올려</button>
</div>
);
};
export default App;
무한루프에 빠지게 된다!!
이유는 올려를 클릭하면 count가 증가되기 때문에 count state가 업데이트가 되기 때문에 useEffect가 불리는데
useEffect안에서도 renderCount를 업데이트시켜주는 코드가 있다.
이 말은
- count state가 업데이트되면
- useEffect 불리고
- renderCount state가 업데이트되고
- 또다시 useEffect가 불리고
- renderCount state가 업데이트되고
... 무한 반복된다.
ref로 해결하면 된다.
import { useState, useEffect, useRef } from "react";
function App() {
return (
<>
<UseRef3 />
</>
);
}
const UseRef3 = () => {
const [count, setCount] = useState(1);
const renderCount = useRef(1);
useEffect(() => {
renderCount.current += 1;
console.log("렌더링 수: ", renderCount.current);
});
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>올려</button>
</div>
);
};
export default App;
맨 처음 렌더링이 2인 이유는 마운트 되었을 때 useEffect가 실행되기 때문에 1이 아닌 2가 된다.
잘되는 이유는 ref는 리렌더링을 발생시키지 않기 때문에 아까와 같이 무한루프에 빠지지 않게 된다.
결론
useRef는 변화는 감지해야 하지만 그 변화가 렌더링을 발생시키면 안 되는 값을 다룰 때 편리하다.
※ 이 글은 별코딩 리액트 훅 강의를 보고 정리한 글입니다.
'React' 카테고리의 다른 글
[React] useMemo (0) | 2023.09.15 |
---|---|
[React] useContext + Context API (0) | 2023.09.15 |
[React] useRef_DOM 요소 접근 (0) | 2023.09.14 |
[React] useEffect (0) | 2023.09.12 |
[React] useState (0) | 2023.09.11 |