일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- recoil
- 프로그래머스
- Suspense
- 리코일
- 완전탐색
- useContext
- 알고리즘 #코딩테스트 #프로그래머스 #자바
- useLayoutEffect
- 위상정렬
- 스티커
- usedebugvalue
- 드래그앤드롭
- 알고리즘
- 백준 #알고리즘 #자바스크립트
- useRef
- 덕타이핑
- React
- 자바스크립트
- 타입스크립트
- react-beautiful-dnd
- javascript
- 리액트
- 다이나믹프로그래밍
- 백준
- 알고리즘 #프로그래머스 #코딩테스트 #자바스크립트
- 프로그래머스 #알고리즘 #자바스크립트
- 훅
- Today
- Total
몽환화
[React] useContext 물고뜯기 본문
또 물고뜯으러 옴
2024.05.01 - [Front/React] - [React] useRef 물고뜯기
[React] useRef 물고뜯기
useRef렌더링에 필요하지 않은 값을 참조할 수 있는 React Hook 컴포넌트의 최상위 레벨에서 useRef를 호출하여 ref를 선언const ref = useRef(initialValue) 컴포넌트가 일부 정보를 “기억”하고 싶지만, 해
intothe-universe.tistory.com
useContext
리액트의 훅을 모두 물어뜯어버리겠ㄷ ㅏ
가보자고
우선 Context란 무엇인가
리액트 애플리케이션은 기본적으로 부모 컴포넌트와 자식 컴포넌트로 이뤄진 트리 구조를 갖고 있다.
부모 가 가지고 있는 데이터를 자식에서도 사용하고 싶다면 props로 넘겨주는 것이 일반적!
그러나 전달해야하는 데이터가 있는 컴포넌트와 전달받아야 하는 컴포넌트가 너무너무 멀다면?
그거 주구장창 props로 전달해야한다 쏘 불편
이를 극복하기 위한 개념이 context
명시적인 props 전달 없이도 선언된 하위 컴포넌트 모두에서 자유롭게 사용할 수 있다!
기존의 context API 를 개선한 것 같은 느낌이네 consumer가 필요없으니
useContext : 상위 컴포넌트에서 만들어진 context를 함수 컴포넌트에서 사용할 수 있게 해주는 리액트 훅
import { createContext } from "react";
export const SomeContext = createContext(null);
const value = useContext(SomeContext)
createContext : context 객체 생성
Provider : 생성한 context를 하위 컴포넌트에 전달 (SomeContext.Provider)
context를 전달하려면 전달하려는 컴포넌트 혹은 그 컴포넌트의 상위 컴포넌트중 하나를 provider로 감싼다
useContext는 호출하는 컴포넌트에 대한 context값을 반환한다. 호출한 컴포넌트에서 트리상 위에 있는 가장 가까운 Provider에 전달된 value를 가져오게 된다. 만약 provider가 존재하지 않는 context를 호출했다면 해당 context가 처음 생성됐을 때, 즉 createContext에 전달된 값을 가져오게 된다.
import { createContext, useContext } from "react";
const Context = createContext<{ hello: string } | undefined>(undefined);
export function ParentComponent() {
return (
<>
<Context.Provider value={{ hello: "react" }}>
<Context.Provider value={{ hello: "javascript" }}>
<ChildComponent />
</Context.Provider>
</Context.Provider>
</>
);
}
export function ChildComponent() {
const value = useContext(Context);
return <>{value ? value.hello : ""}</>;
}
여기서는 가장 가까운 값인 javascript를 가져오게 되는 것이다
다만 useContext로 원하는 값을 얻어오려고 했는데 해당 context가 존재하지 않는다면 에러가 발생한다
const context = useContext(Context)
if(context === undefined){
throw new Error()
}
그러므로 먼저 useContext 내부에서 해당 context가 존재하는 환경인지 확인하는 과정을 거쳐줄 필요가 있다
(모던 리액트 Deep Dive 예시 코드)
import {
ChangeEvent,
PropsWithChildren,
createContext,
useContext,
useEffect,
useState,
} from "react";
const Context = createContext<{ hello: string } | undefined>(undefined);
function ContextProvider({
children,
text,
}: PropsWithChildren<{ text: string }>) {
return (
<Context.Provider value={{ hello: text }}>{children}</Context.Provider>
);
}
function useMyContext() {
const context = useContext(Context);
if (context === undefined) {
throw new Error("안됑");
}
return context;
}
function GrandChildComponent() {
const { hello } = useMyContext();
useEffect(() => {
console.log("렌더링 grandchild");
});
return <h3>{hello}</h3>;
}
function ChildComponent() {
useEffect(() => {
console.log("렌더링 child");
});
return <GrandChildComponent />;
}
export function ParentComponent() {
const [text, setText] = useState("");
function handleChange(e: ChangeEvent<HTMLInputElement>) {
setText(e.target.value);
}
useEffect(() => {
console.log("렌더링 parent");
});
return (
<>
<ContextProvider text="react">
<input value={text} onChange={handleChange} />
<ChildComponent />
</ContextProvider>
</>
);
}
useContext를 사용할 때 주의할 점
- 컴포넌트의 재활용이 어려워진다. useContext가 선언된 이상 Provider에 의존성을 가지고 있게 된다 => 그러한 context를 가지고 있지 않는 곳이라면 재활용이 어렵다
- 컴포넌트의 useContext() 호출은 동일한 컴포넌트에서 반환된 provider의 영향을 받지 않습니다. 해당 <Context.Provider>는 반드시 useContext() 호출을 수행하는 컴포넌트의 위에 있어야 합니다.
- context의 값이 변동되면 해당 context로 감겨있는 모든 컴포넌트들이 리렌더링 된다. useContext가 최적화에 도움이 되는 것은 아니다. 단순히 props 값을 하위로 전달전달 해줄 뿐!
그렇다면 여기서 굳이 리렌더링 하지 않아도 되는 컴포넌트들의 리렌더링을 막는 방법은?
memo를 사용한다
memo : 렌더링할 컴포넌트에 해당하는 props와 다음 props를 비교하여 재 호출 여부를 결정한다
props의 변화가 없다면 리렌더링되지 않을 거임
// 메모 사용 ㄴㄴ
function ChildComponent() {
useEffect(() => {
console.log("렌더링 child");
});
return <GrandChildComponent />;
}
// 메모 사용 ㅇㅇ
const ChildComponent = memo(() => {
useEffect(() => {
console.log("렌더링 child");
});
return <GrandChildComponent />;
});
이 memo 함수의 두번째 인자로 콜백을 전달하지 않으므로 ChildComponent는 자신의 상태를 변경하지 않는 이상 다시 호출되지 않을 것임
첫 렌더링 이후 저 child는 context의 변화가 생겨도 렌더링 안됨
참고자료
도서 :: 모던 리액트 Deep Dive
https://hokeydokey.tistory.com/205
https://velog.io/@jminkyoung/TIL-6-React-Hooks-useContext-%EB%9E%80
'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] useRef 물고뜯기 (1) | 2024.05.01 |