✓ useRef
- https://react.dev/reference/react/useRef
- https://ko.react.dev/reference/react/useRef
- React Hook that lets you reference a value that’s not needed for rendering.
렌더링이 필요없는 값을 참조할 수 있도록 레퍼런스를 제공하는 훅
import { useRef } from 'react';
const ref = useRef(initialValue);
- Call useRef at the top level of your component to declare a ref.
- initialValue: The value you want the ref object’s current property to be initially. It can be a value of any type. This argument is ignored after the initial render.
ref 객체의 초기값, 어떤 타입이던지 가능하며 첫번째 렌더이후 이 argument는 무시됨
- useRef returns an object with a single property:
current: Initially, it’s set to the initialValue you have passed. You can later set it to something else. If you pass the ref object to React as a ref attribute to a JSX node, React will set its current property.
On the next renders, useRef will return the same object.
하나의 프로퍼티 current만 return
처음에는 사용자가 전달한 initialValue로 세팅되나 변경 가능, jsx 노드의 어트리뷰트로 전달 가능
다음 렌더링에서 동일한 객체 반환 (리렌더링 X)
- Caveats
1) You can mutate the ref.current property. Unlike state, it is mutable. However, if it holds an object that is used for rendering (for example, a piece of your state), then you shouldn’t mutate that object.
ref.current는 state와 달리 변경 가능, 하지만 렌더링에 사용하는 객체의 경우 변경하면 안됨 -> 이벤트 핸들러 이용
2) When you change the ref.current property, React does not re-render your component. React is not aware of when you change it because a ref is a plain JavaScript object
ref.current 속성은 변경할 수 있으나 이는 자바스크립트 객체라 리액트가 변화를 인식할 수 없으므로, 리렌더링 X
3) Do not write or read ref.current during rendering, except for initialization. This makes your component’s behavior unpredictable.
초기화 단계 제외 렌더링중 ref.current 읽기 및 쓰기 금지 -> 예측 불가능한 오류 발생 가능
4) In Strict Mode, React will call your component function twice in order to help you find accidental impurities. This is development-only behavior and does not affect production. Each ref object will be created twice, but one of the versions will be discarded. If your component function is pure (as it should be), this should not affect the behavior.
strict mode에서는 2번 call되나 이는 개발모드 한정. ref 객체가 두 번 생성되고, 하나는 무시됨.
- Usage
1) Referencing a value with a ref : 값 참조
import { useRef } from 'react';
function Stopwatch() {
const intervalRef = useRef(0);
// ...
function handleStartClick() {
const intervalId = setInterval(() => {
// ...
}, 1000);
intervalRef.current = intervalId;
}
function handleStopClick() {
const intervalId = intervalRef.current;
clearInterval(intervalId);
}
}
useRef returns a ref object with a single current property initially set to the initial value you provided.
On the next renders, useRef will return the same object. You can change its current property to store information and read it later. This might remind you of state, but there is an important difference.
current 프로퍼티만 가진 ref 객체를 return하고, 렌더링 이후에도 같은 객체 리턴.
current 프로퍼티에 정보를 저장하고 나중에 읽을 수 있음(state랑은 다름)
Changing a ref does not trigger a re-render. This means refs are perfect for storing information that doesn’t affect the visual output of your component. For example, if you need to store an interval ID and retrieve it later, you can put it in a ref. To update the value inside the ref, you need to manually change its current property:
리렌더링을 트리거 하는게 아님. 즉, 컴포넌트의 비쥬얼적인 요소를 변경시키는 게 아님. 정보를 저장하기 위한 것.
By using a ref, you ensure that:
- You can store information between re-renders (unlike regular variables, which reset on every render).
- Changing it does not trigger a re-render (unlike state variables, which trigger a re-render).
- The information is local to each copy of your component (unlike the variables outside, which are shared).
Changing a ref does not trigger a re-render, so refs are not appropriate for storing information you want to display on the screen. Use state for that instead. Read more about choosing between useRef and useState.
리렌더 간 정보 저장용, 리렌더 트리거 X, 컴포넌트의 local information copy
examples) Click Counter 카운터 횟수 저장 / Stop Watch
* Do not write or read ref.current during rendering.
-> If you have to read or write something during rendering, use state instead.
// bad example
function MyComponent() {
// ...
// 🚩 Don't write a ref during rendering
myRef.current = 123;
// ...
// 🚩 Don't read a ref during rendering
return <h1>{myOtherRef.current}</h1>;
}
// good example
function MyComponent() {
// ...
useEffect(() => {
// ✅ You can read or write refs in effects
myRef.current = 123;
});
// ...
function handleClick() {
// ✅ You can read or write refs in event handlers
doSomething(myOtherRef.current);
}
// ...
}
2) Manipulating the DOM with a ref : DOM 조작
import { useRef } from 'react';
function MyComponent()
// First, declare a ref object with an initial value of null:
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
// Then pass your ref object as the ref attribute to the JSX of the DOM node you want to manipulate:
return <input ref={inputRef} />;
}
After React creates the DOM node and puts it on the screen, React will set the current property of your ref object to that DOM node. Now you can access the <input>’s DOM node and call methods like focus():
useRef(null) 로 초기화 -> input ref 프로퍼티 -> DOM 렌더링 이후 ref.current : DOM 노드 반환
React will set the current property back to null when the node is removed from the screen.
노드가 화면에서 사라지면 ref.current = null로 세팅
examples)
- Focusing a text input
- Scrolling an image into view : view로 이미지 스크롤
- Playing and pausing a video : play or pause 정보로 활용
- Exposing a ref to your own component : parent + child 컴포넌트 조합 시 useRef + forwardRef 활용
3) Avoiding recreating the ref contents : ref 컨텐츠를 재생성하는 것 방지
function Video() {
// expensive code -> on every render
// const playerRef = useRef(new VideoPlayer());
function getPlayer() {
if (playerRef.current !== null) {
return playerRef.current;
}
// How to avoid null checks when initializing useRef later
const player = new VideoPlayer();
playerRef.current = player;
return player;
}
}
✓ useContext
- https://react.dev/reference/react/useContext
- https://ko.react.dev/reference/react/useContext
- React Hook that lets you read and subscribe to context from your component.
컴포넌트가 context를 구독하고 읽을 수 있도록 도와주는 리액트 훅
import { useContext } from 'react';
const value = useContext(SomeContext);
- Call useContext at the top level of your component to read and subscribe to context.
- SomeContext: The context that you’ve previously created with createContext. The context itself does not hold the information, it only represents the kind of information you can provide or read from components.
createContext로 이전에 만든 context, context 자체는 정보를 가지고 있지 않고 단지 사용자가 컴포넌트를 읽거나 제공할 수 있는 정보의 종류를 나타냄
- Returns
the context value for the calling component. It is determined as the value passed to the closest SomeContext.Provider above the calling component in the tree. If there is no such provider, then the returned value will be the defaultValue you have passed to createContext for that context. The returned value is always up-to-date. React automatically re-renders components that read some context if it changes.
호출하는 컴포넌트에 대한 context value 리턴, 해당 컴포넌트 트리에서 가장 가까운 SomeContext.Provider에서 전달받은 값
provider가 없으면 createContext에서 생성된 context의 defaultValue, 리턴되는 value는 항상 최신 값.
리액트는 해당 컨텍스트가 변경되면 그 컨텍스트를 참조하는 컴포넌트를 리렌더함
- Caveats
1) useContext() call in a component is not affected by providers returned from the same component. The corresponding <Context.Provider> needs to be above the component doing the useContext() call.
useContext는 같은 컴포넌트에서 반환되는 provider의 영향을 받지 않으며, context.provider는 반드시 useContext를 호출하는 컴포넌트 상위에 배치되어야 함
2) React automatically re-renders all the children that use a particular context starting from the provider that receives a different value. The previous and the next values are compared with the Object.is comparison. Skipping re-renders with memo does not prevent the children receiving fresh context values.
리액트는 다른 value를 받아 provider로 시작해서, 특정 context를 사용하는 모든 childeren을 자동으로 리렌더
이전 값과 다음 값은 Object.is에 의해 비교됨, memo로 리렌더를 스킵해도 children은 context의 새로운 값을 받음
3) If your build system produces duplicates modules in the output (which can happen with symlinks), this can break context. Passing something via context only works if SomeContext that you use to provide context and SomeContext that you use to read it are exactly the same object, as determined by a === comparison.
빌드 시스템이 결과적으로 중복 모듈을 생성하는 경우(심볼릭 링크), 컨텍스트가 파괴될 수 있음.
context를 통해 무언가를 전달하는 것은 === 비교에 의해 결정되는 것처럼, 컨텍스트를 제공하는 데 사용하는 SomeContext와 context를 읽는 데 사용하는 SomeContext가 정확하게 동일한 객체인 경우에만 작동
- Usage
1) Passing data deeply into the tree : 트리 깊은 곳까지 값 전달
import { useContext } from 'react';
function MyPage() {
return (
<ThemeContext.Provider value="dark">
<Form />
</ThemeContext.Provider>
);
}
function Form() {
// ... renders buttons inside ...
function Button() {
const theme = useContext(ThemeContext);
// ...
}
}
useContext returns the context value for the context you passed. To determine the context value, React searches the component tree and finds the closest context provider above for that particular context. It doesn’t matter how many layers of components there are between the provider and the Button. When a Button anywhere inside of Form calls useContext(ThemeContext), it will receive "dark" as the value.
전달한 컨텍스트에서 값을 리턴, 컴포넌트에서 가장 가까운 context provider를 찾는다.
컴포넌트 레이어가 몇 겹이던지 상관없이 useContext를 호출한 곳을 찾아 value 리턴
useContext() always looks for the closest provider above the component that calls it. It searches upwards and does not consider providers in the component from which you’re calling useContext().
useContext를 호출한 컴포넌트와 동일한 계층이 아니라, 무조건 상위 계층에서 가까운 provider를 찾음
2) Updating data passed via context : 컨텍스트를 통해 데이터(state) 업데이트
function MyPage() {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext.Provider value={theme}>
<Form />
<Button onClick={() => {
setTheme('light');
}}>
Switch to light theme
</Button>
</ThemeContext.Provider>
);
}
a. Updating a value via context
b. Updating an object via context
c. Multiple contexts
d. Extracting providers to a component
e. Scaling up with context and a reducer : ex) 동적으로 input 추가하는 로직
3) Specifying a fallback default value : fallback 기본값 지정 -> context.provider를 찾을 수 없을 때
// const ThemeContext = createContext(null);
const ThemeContext = createContext('light');
If React can’t find any providers of that particular context in the parent tree, the context value returned by useContext() will be equal to the default value that you specified when you created that context:
The default value never changes. If you want to update context, use it with state as described above.
Often, instead of null, there is some more meaningful value you can use as a default.
리액트가 parent 트리에서 context를 찾지 못하면, useContext의 default value 반환.
default value로 null 대신 의미있는 단어 사용 권장
4) Overriding context for a part of the tree : 트리의 일정 부분 오버라이드 -> 컨텍스트 중첩 사용
5) Optimizing re-renders when passing objects and functions : 객체와 함수를 전달해 리렌더링 최적화
function MyApp() {
const [currentUser, setCurrentUser] = useState(null);
function login(response) {
storeCredentials(response.credentials);
setCurrentUser(response.user);
}
return (
<AuthContext.Provider value={{ currentUser, login }}>
<Page />
</AuthContext.Provider>
);
}
Here, the context value is a JavaScript object with two properties, one of which is a function. Whenever MyApp re-renders (for example, on a route update), this will be a different object pointing at a different function, so React will also have to re-render all components deep in the tree that call useContext(AuthContext).
login이 function object이기 때문에 매번 다른 주소를 가리키게 됨 -> 리액트는 트리 내부의 useContext를 호출하는 모든 컴포넌트 리렌더
import { useCallback, useMemo } from 'react';
function MyApp() {
const [currentUser, setCurrentUser] = useState(null);
const login = useCallback((response) => {
storeCredentials(response.credentials);
setCurrentUser(response.user);
}, []);
const contextValue = useMemo(() => ({
currentUser,
login
}), [currentUser, login]);
return (
<AuthContext.Provider value={contextValue}>
<Page />
</AuthContext.Provider>
);
}
To help React take advantage of that fact, you may wrap the login function with useCallback and wrap the object creation into useMemo.
useMemo랑 useCallback으로 감싸서 컴포넌트 최적화
'React' 카테고리의 다른 글
240706 리액트 스터디 : useRef (0) | 2024.07.07 |
---|---|
240629 리액트 스터디 : useRef, useContext (0) | 2024.06.29 |
240622 리액트 스터디 : useMemo (0) | 2024.06.22 |
Deep Dive : UseMemo, UseCallback (0) | 2024.06.22 |
240615 리액트 스터디 : hooks (0) | 2024.06.15 |