[React][Context] Prop drilling 과 useContext (2/2)

[React][Context] Prop drilling 과 useContext (2/2)

반응형

https://snupi.tistory.com/185

useContext

useState, useEffect와 함께 기본 Hook인 useContext에 대해 알아봅시다.

Context 개념

Context 란?

그러한 데이터로는 현재 로그인한 유저, 테마, 선호하는 언어, 데이터 캐시 등이 있습니다.

// context를 사용하면 모든 컴포넌트를 일일이 통하지 않고도 // 원하는 값을 컴포넌트 트리 깊숙한 곳까지 보낼 수 있습니다. // light를 기본값으로 하는 테마 context를 만들어 봅시다. **const ThemeContext = React.createContext('light');** class App extends React.Component { render() { // Provider를 이용해 하위 트리에 테마 값을 보내줍니다. // 아무리 깊숙히 있어도, 모든 컴포넌트가 이 값을 읽을 수 있습니다. // 아래 예시에서는 dark를 현재 선택된 테마 값으로 보내고 있습니다. return ( ); } } // 이젠 중간에 있는 컴포넌트가 일일이 테마를 넘겨줄 필요가 없습니다. function Toolbar() { return ( ); } class ThemedButton extends React.Component { // 현재 선택된 테마 값을 읽기 위해 contextType을 지정합니다. // React는 가장 가까이 있는 테마 Provider를 찾아 그 값을 사용할 것입니다. // 이 예시에서 현재 선택된 테마는 dark입니다. **static contextType = ThemeContext;** render() { return ; } }

사용 형태로 보아, 우리가 흔히 사용하던 ThemeProvider 역시 Context API 를 사용하는 것을 유추할 수 있습니다.

import { ThemeProvider } from "styled-components"; import theme from "./styles/theme"; import Router from "components/Router"; const App = () => { return ( ); }; export default App;

API 사용

React.createContextContext

const SnupiContext = React.createContext(defaultValue);

Context 객체를 만든다.

React는 트리 상위에서 가장 가까이 있는 짝이 맞는 Provider로부터 값을 읽는다.

즉, 상위 Provider보다 하위 Provider의 값이 우선시된다.

찾지 못했을 때만, defaultValue 값이 쓰인다.

Context.ProviderContext

Context 객체에 포함된 Provider는 context의 변화를 구독하는 컴포넌트들에게 알린다.

Provider 하위에서 context를 구독하는 모든 컴포넌트는 Provider의 value prop가 바뀔 때마다 다시 렌더링 된다.

Provider로부터 하위 consumer로의 전파는 shouldComponentUpdate 메서드가 적용되지 않으므로, 상위 컴포넌트가 업데이트를 건너 뛰더라도 consumer가 업데이트 된다.

Context.Consumer

{value => /* context 값을 이용한 렌더링 */}

Consumer의 자식으로는 함수가 와야 한다.

이 함수는 **context의 현재 값(value)**을 받고 React 노드를 반환하게 된다.

~~Context.displayName~~

~~Class.contextType~~

대체 방법: Inversion of Control / Render Props

API 사용법

애플리케이션의 데이터 저장소인 context는 다음과 같이 만든다.

// contexts/index.js import React from "react"; export const SnupiContext = React.createContext();

// contexts/ContentProvier.jsx import React, { useState } from "react"; import { SnupiContext } from "."; function UserContextProvider({ children }) { const [user, setUser] = useState({ name: "Snupi", loggedIn: false, }); const logUserIn = () => setUser({ ...user, loggedIn: true }); return ( {children} ); }; export default UserContextProvider;

React.createContext() 메소드로 context를 생성하고, state 값을 value로 가진 태그 안에 {children} 을 넣어 리턴한다.

이후에 App.jsx에서는

// App.jsx import Router from "./Components/Router"; import UserContextProvider from "./contexts/ContextProvider"; function App() { return ( ); } export default App;

위와 같이 나타낼 컴포넌트를 UserContext.Provider 태그 안에 {children}으로 넣어준다.

이후 컴포넌트 안에서는 react의 { useContext } 모듈과, context.js에서 export한 { userContext } 모듈로 다음과 같이 사용한다.

const { user: { name, loggedIn } } = useContext(UserContext); // or const { logUserIn } = useContext(UserContext);

쓸 때마다 useContext를 사용할 필요 없이, 다음과 같이 contexts 한 파일에서 자동으로 선언해 줄 function을 만들어 줄 수 있다.

// contexts/index.js import React, { useContext } from "react"; export const SnupiContext = React.createContext(); export const useUser = () => { const { user } = useContext(SnupiContext); return user; }; export const useFunction = () => { const { logUserIn } = useContext(SnupiContext); return logUserIn; };

이를 통해 다른 파일에서 다음과 같이 useContext(userContext)를 쓰지 않고 함수로 선언해 줄 수 있다.

const { name, loggedIn } = useUser(); const { logUserIn } = useFunction();

// context 사용 컴포넌트 import React from "react"; import { useFunction, useUser } from "../contexts"; const PrtContext = () => { const { name, loggedIn } = useUser(); const logUserIn = useFunction(); return ( Hello, {name}, you are {loggedIn ? "logged in" : "anonymous"} Log user in ); }; export default PrtContext;

위와 같이 많은 데이터를 처리해야 할 때면 데이터를 변경하는 function도 많아지므로,

데이터를 제공하는 Data Provider, 데이터를 변경하는 Provider로 2개 나누는 것이 좋다.

또는 데이터를 종류 별로 나누는 것도 좋다.

마지막으로, context 값이 변경됨으로써 리렌더링 하는 것에 비용이 많이 든다면, useMemo 등의 메모이제이션을 사용하여 최적화할 수 있다. (https://github.com/facebook/react/issues/15156#issuecomment-474590693)

장점

Redux와 비교하여, 다음과 같은 장점이 있다.

Built-in

러닝 커브가 낮다

전역 상태가 아닌, Single State Tree에서 유용하다

Lifecycle 관리가 유용하다 Context는 Redux와 달리, React 컴포넌트 트리 상에 존재하기 때문에, Provider 컴포넌트가 unmount 되면 함께 소멸된다.

단점

React 컴포넌트이기 때문에 생길 수 있는 Wrapper Hell

컨벤션/편의 도구의 부재

참고 자료:

React에서 Prop Drilling과 해결 방법

프로퍼티 내리꽂기 (prop drilling)

리액트 컨텍스트

Context - React

상태 관리 도구로서의 React Context

반응형

from http://snupi.tistory.com/186 by ccl(A) rewrite - 2021-12-31 15:00:54