https://react.dev/learn/referencing-values-with-refs를 읽고 정리한다.
컴포넌트가 어떤 정보를 "기억"해야 하지만, 리 렌더링이 트리거 되지 않기를 원한다면, `ref`를 사용하면 된다.
React의 `useRef` Hook을 이용해 컴포넌트에 `ref`를 추가할 수 있다.
- 컴포넌트 내부에서 `useRef` 훅을 호출할 수 있으며, 오직 argument를 통해 초기값을 설정할 수 있다.
- `ref.current` 프로퍼티를 통해 ref의 현재 값에 접근할 수 있다.
- 이 값은 언제든 변할 수 있다. 즉, 읽고 쓰기가 둘 다 가능하다.
- 리액트가 추적하지 않는 값이다.
- 단방향 데이트 흐름을 가진 리액트로부터의 탈출 수단(escape hatch)이 되는 방법이다.
- ref는 읽고 수정할 수 있는 `current` 프로퍼티를 가진 js Object 객체이다.
- 어떤 값도 사용할 수 있다.
- state처럼 리 렌더링 간에 값이 유지된다. 하지만 리 렌더링을 트리거하지는 않는다.
ref와 state의 차이
ref | state |
`useRef(initialValue)`의 반환 값 => `{ current: initialValue }` |
`useState(initialValue)`의 반환 값 => `[value, setValue]` : 현재 값과 state setter 함수 |
변경되도 리렌더링을 트리거하지 않음 | 변경되면 리렌더링이 트리거됨 |
mutable(변경가능한 값) - 렌더링 프로세스 외부에서 `current`의 값을 수정하고 업데이트 가능하다. |
immutable(불변 값) - state setter 함수를 사용해 state 변수를 수정하고, 리렌더링 대기열에 추가된다. |
렌더링할 때(in JSX) `current`값을 읽거나 쓸 수 없다. | 언제든 state를 읽을 수 있다. 하지만, 각 렌더는 고유의 변경되지않는 state의 스냅샷을 가지고 있다. |
내부에서 useRef는 어떻게 동작하는 가?
React 내부에서 아래처럼 구현된다고 상상할 수 있다.
// Inside of React function useRef(initialValue) { const [ref, unused] = useState({ current: initialValue }); return ref; }
첫 렌더에서, `useRef`는 `{ current: initialValue }`를 리턴한다. 이 객체는 react로부터 저장되므로, 다음 렌더링 때도 동일한 객체가 반환된다.
`useRef`는 항상 동일한 객체를 반환해야 하기 때문에 state setter 함수는 불필요하다.
`useRef`는 React에서 내장되어 제공된다. 하지만 위와 같이 setter가 없는 일반 state 변수로 생각할 수도 있다.
ref를 사용하는 곳
컴포넌트의 외부 API와 통신과 같은 React 외부에서 무언가를 해야할 때 ref를 사용한다.
- React 외부에서 무언가를 해야할 때
- 외부 API와 통신할 때 ex) 컴포넌트의 모양에 영향을 주지 않는 브라우저 API
- timeout ID를 저장할 때
- DOM element 조작하거나 저장할 때
- JSX 계산이 필요없는 다른 객체를 저장할 때
만약 컴포넌트가 값을 저장해야 하는 데, 렌더링 로직에 영향을 끼치지 않는다면 ref를 사용한다.
ref를 위한 Best practices
아래의 원칙을 따르면 컴포넌트를 보다 예측 가능하게 만들 수 있다.
- ref를 탈출구로만 사용하기. ref는 외부 시스템과 browser API와 함께 작업할 때 유용하다. 앱의 많은 로직과 데이터 플로우가 ref에 의존적이라면, 접근 방법을 다시 생각해봐야 한다.
- 렌더링 도중
ref.current
를 읽고 쓰지 말기. 렌더링 할 때 여러 정보가 필요하다면, ref 대신 state 사용해라. 리액트는ref.current
가 변경될 때를 모르기 때문에 렌더링 동안ref.current
를 읽는 것은 컴포넌트의 행동이 예측하기 어렵게 만든다.- 예외적으로 아래 코드처럼 첫 번째 렌더에서 한 번만 ref를 설정하는 동작은 가능하다.
if(!ref.current) {
ref.current = new Thing();
}
리액트의 state에서 한계는 refs에 적용되지 않는다. 예를 들어 state는 모든 렌더링을 위한 스냅숏인 것처럼 행동하고 동기적으로 업데이트되지 않는다. 그러나 ref의 current value는 변경할 수 있는 값이어서 즉시 값을 변경할 수 있다.
이에 대한 예제로 Challeges > 4.Read the latest state를 참고할 수 있다.
- 같은 값(input text)에 대해 state, ref 모두 사용한다.
- state 사용 이유 : 렌더링할 때 현재 input text 값을 사용
- ref 사용 이유 : timeout과 같은 비동기 연산 최신 input text 값을 읽기 위해
- 이 때, 최신 state는 읽을 수 없기때문에 ref를 사용해 최신 값을 저장하고 사용
ret 자체가 일반적인 JavaScript 객체이기 때문에, 객체처럼 동작한다.
또한 ref를 사용할 때 mutation을 피하는 것에 대해 걱정할 필요가 없다. 변경하는 객체를 렌더 동안 사용하지 않는 한, 리액트는 ref와 내부 콘텐츠에 무엇을 하든 상관하지 않는다.
ref와 DOM
ref는 어떤 값이든 가리킬 수 있다. 그러나, ref에서 가장 일반적인 유스케이스는 DOM 요소를 접근하는 것이다. 예를 들어, input을 프로그램에 따라 포커스를 주고 싶을 때 편리하다. <div ref={myRef}>
처럼 JSX의 ref
속성에 ref를 전달할 때, 리액트는 해당하는 DOM 요소를 myRef.current
에 넣는다. (더 알아보려면 ref를 사용해 DOM 조작하기를 읽기)
'React > Learn React' 카테고리의 다른 글
Describing the UI ☞ Conditional Rendering (0) | 2023.03.05 |
---|---|
Adding Interactivity ☞ Queueing a Series of State Updates (0) | 2022.12.04 |
Managing State ☞ Scaling Up with Reducer and Context (0) | 2022.10.31 |
Managing State ☞ Passing Data Deeply with Context (0) | 2022.10.30 |
Managing State ☞ Extracting State Logic into a Reducer (1) | 2022.10.03 |