일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- jest
- 호키도키
- IOS
- 자스민
- hokeys
- react
- 스벨트
- SWIFT
- 자바스크립트 자료구조
- hokidoki
- 계명대
- Svelte
- 계명대 이종호
- queue
- 호키스
- data structure
- HTML
- 비동기
- 이종호
- 자바스크립트
- 힛잇
- 스위프트
- 자료구조
- 개발자
- 리액트
- 개발
- TDD
- Hitit
- 리액트 예제
- javascript
- Today
- Total
Dog foot print
[React] Hook기본 다지기#3 context 본문
서론
App의 컴포넌트 트리가 혹시 길어지는 것을 경험 해본 적이 있는가 ? 짧은 토이 프로젝트의 경우 컴포넌트 노드의 레벨이 3정도 될 것이다. 이런 경우 root component에서 최 하위 컴포넌트 노드에게 정보를 전달 해주기 위해, props를 각 레벨의 컴포넌트 노드에게 전달 해준 경험이 존재 할 것이다. 그렇게 어려운 일도 아니고, props를 각 컴포넌트에게 전달 하면 장땡이다 !
이처럼 짧은 거리의 컴포넌트에게는 props로 데이터를 전달해주는 것은 충분하다. 그러나 트리의 높이가 4이상을 넘어가게 되면, props로 상위 컴포넌트의데이터를 전달해주는 것은 매우 힘들다 .
불가능하다는 이야기가 아니다. 코드가 지저분해지고, 이를 하위 프롭스로 전달하는 과정에서, 개발자의 실수로 유지하지 못할 수 있다.
context
배워야 할 것들이 ~context API이니, 잠시 context가 무엇인지 조금만 짚고 넘어가보자. Context를 한국어로 직역하면, 문맥이 된다. 단순히 문맥이라고한다면 이해하기 매우 어려우니, 쉽게 접근 가능한 영역이라고 하면 좋을 것 같다.
누군가는 이렇게 이야기 할 것이다. “ 컴퓨터 공학부에서 배운 컨텍스트는 멀티 태스킹환경에서 Task가 변경 될 때 변경되는 Task의 실행 상태 정보를 담고있는 것 입니다. “ 맞는 말이다. 이 컨텍스트라는 단어는 언어 차원의 Context와 컴퓨터 공학에서 다루는 Context의 두가지 의미가 존재한다. 여기서는 주로언어 차원에서 context에 대해서 다룬다.
const App = () => {
const a = 10;
Childe();
}
const Childe = () => {
console.log(a);
}
예를 들어 위와 같이 App함수내에서 선언된 a변수를 Childe함수에서 접근하려 하면, 어떤 일이 발생할까 ? 당연히 같은 스코프를 공유하지 않기 때문에Childe입장에서는 a변수에 접근이 불가능하다. (결과는 undefined) 그렇다면 App 변수의 Context를 Childe에게 유지 시켜 전달하려면 어떻게 할까 ? (매개변수를 사용하지 않고 ! )
const App = () => {
const a = 10;
const Childe = () => {
console.log(a);
}
Childe();
}
이렇게 Childe함수를 App의 함수에서 선언하여, 캡처를 이용하는 방법도 존재한다. 그러나, 파일 별로 컴포넌트를 작성하는 리액트에서는 자주 사용되지 않는다. 캡처를 이용한 컨텍스트 공유를 하는 경우가 있지만 컴포넌트의 코드 흐름을 방해하지 하지 않도록 이 방법은 남발해서는 안된다.
또 다른 방법으로는 무엇이 있을까 ? 전역적으로 관리되는 변수를 이용하는 방법이 존재한다.
let a = 10;
const App = () => {
a = 20;
}
const Childe = () => {
console.log(a);
}
Childe();
React에서 기본으로 제공되는 Context api는 이렇게 전역적으로 관리되는 변수에 사이드 이펙트를 주어, 이를 하위 컴포넌트에서 구독하는 방법으로, 상위컴포넌트에서 전달해야하는 컨텍스트를 하위 컴포넌트에서도 읽기 가능하다.
Create Context
createContext 함수는 리액트에서 제공하는 contextAPI이다. createContext 함수의 기본 구조는 다음과 같다. createContext(defaultValue) => {Provider, Consumer}
Provider 컴포넌트는 Context를 구독하고 있는 하위 컴포넌트에게 value를 전달하는 역할을 한다. Consumer 컴포넌트는 Context를 구독하고자 하는 하위 컴포넌트가 상위 컴포넌트에게 value를 전달 받는 역할을 한다.
/src 에 Profile.js를 만들어 잠시 준비해 보자.
import React from 'react'
export default function Profile() {
return (
<div>
<Hello></Hello>
</div>
)
}
function Hello(){
return (
<>
<h1>hello !</h1>
</>
)
}
App 컴포넌트에서 Hello 컴포넌트까지의 경로는 App -> Profile -> Hello순이다. Props 또한 이와 같이 전달해주어야 Hello컴포넌트에서 사용 할 수 있다.
이제 /src/app.js에서 context를 만들어 보도록 하자.
import './App.css';
import React, {useState,useEffect,createContext} from 'react'
import {useCDM} from './hooks/lifecycle'
import Profile from './Profile';
import { getInitData } from './api/api';
export const UserDataContext = createContext("");
function App() {
const [name,setName] = useState('');
const appInitialize = () => {
getInitData("blue").then((initData)=>{
console.log(initData);
setName(initData);
}).catch((error)=>{
console.log(error);
})
}
const isMount = useCDM(appInitialize);
return (
<div className="App">
<UserDataContext.Provider value={name}>
<Profile></Profile>
</UserDataContext.Provider>
</div>
);
}
UserDataContext에는 defaultValue로 빈 스트링을 할당해주었다. 그리고 Provider에 value props에는 name state를 전달하도록 한다.
이제 /Profile.js에서 구독을 해보도록 하자.
import React from 'react'
import { UserDataContext } from './App';
export default function Profile() {
return (
<div>
<Hello></Hello>
</div>
)
}
function Hello() {
return (
<>
<UserDataContext.Consumer>
{
name => <h1>{name}</h1>
}
</UserDataContext.Consumer>
</>
)
}
이렇게 Props를 트리를 타고 전달하지 않아도, Context API를 통해서 Props가 하위 컴포넌트에게 전달 가능한 것을 알 수 있다.
(결과)
useContext
지금 본 예제에서는 하위 컴포넌트에서 Consumer를 사용하기 때문에, JSX문법이 꽤나 길어져 가독성이 떨어질 우려가 존재한다. 이럴 때 함수에서context를 변수에 할당하여 사용 할 수 있는 useContext API가 존재한다.
/Profile.js
import React,{useContext} from 'react'
import { UserDataContext } from './App';
export default function Profile() {
return (
<div>
<Hello></Hello>
</div>
)
}
function Hello() {
const userName = useContext(UserDataContext)
return (
<>
<h1>{userName}</h1>
</>
)
}
(동일한 결과)
memo
우리가 context API를 사용하면서, Provider로 props를 전달하고자, 하는 하위 컴포넌트를 감쌌다. 그럼 state가 변경 될 때 Context로 직접 구독하지 않은 상위 컴포넌트 또한 다시 렌더링이 될까 ? 답은 “ 그렇다. “
/Profile.js에서 정말 렌더링이 다시 발생하는지 console.log()를 실행해보자 .
export default function Profile() {
console.log("rendering")
return (
<div>
<Hello></Hello>
</div>
)
}
결과는 렌더링이 발생한다.
이 Profile 컴포넌트는 상태변화가 전혀 없기 때문에 굳이 render를 발생 시켜, 자원을 소모 시킬 이유가 없다.
React.memo 즉 memo api를 사용한다면, props나 state가 변경된 사항이 존재하지 않다면, 해당 컴포넌트를 새롭게 렌더링 되지 않는다.
import React,{useContext} from 'react'
import { UserDataContext } from './App';
function Profile() {
console.log("render")
return (
<div>
<Hello></Hello>
</div>
)
}
function Hello() {
const userName = useContext(UserDataContext)
return (
<>
<h1>{userName}</h1>
</>
)
}
const MemorizedProfile = React.memo(Profile);
export default MemorizedProfile
(결과는 단 한번의 render만 된다.)
'REACT' 카테고리의 다른 글
React resolve + css/sass + typescript 적용하기 (0) | 2023.03.15 |
---|---|
React 시작 환경 구축하기 (0) | 2023.03.11 |
[React] Hook기본 다지기#2 custom hook. (0) | 2021.03.28 |
[React] Hook기본 다지기#1 Use Effect (0) | 2021.03.22 |
[React] Hook기본 다지기#0 Use State (0) | 2021.03.21 |