REACT/[inflearn] 처음만난 리액트

14강. Context

web_seul 2022. 12. 2. 13:33
반응형

Context란?

컴포넌트의 props를 통한 데이터 전달하여 모든 단계를 거쳐야하는 기존 방식의 단점을 보완하여 컴포넌트를 직접 전달, 여러 컴포넌트에서 자주 필요로 하는 데이터에 사용

기존방식 vs Context 사용

Context를 사용하는 경우

여러개의 Components들이 접근해야하는 데이터 (ex. 로그인정보, 로그인여부, UI테마, 현재 언어 등)

 

function App(props){
    return <Toolbar theme="dark" />;
}

function Toolbar(props){
	//이 Toolbar 컴포넌트는 ThemedButton에 theme를 넘겨주기 위해서 'theme' props를 가져감
    //현재 테마를 알아야하는 모든 버튼에 대해서 props로 전달하는것은 비효율
    return(
        <div>
            <ThemedButton theme={props.theme} /> {/* 호출1 */}
        </div>
    );
}
function ThemedButton(props){
    return <Button theme={props.theme} />;{/* 호출2 */}
}

{/* 호출3 */}
{/* 호출4 */}
..

 

//컨텍스트는 데이터를 매번 컴포넌트를 통해 전달할 필요없이 컴포넌트트리로 곧바로 전달
//현재 테마를 위한 컨텍스트를 생성하며 기본값은 'light'
const ThemeContext = React.createContext('light');

//provider를 사용하여 하위 컴포넌트들에 현재 테마 데이터 전달
//모든 하위 컴포넌트들은 컴포넌트 트리 하단위치와 무관하게 데이터 접근 가능
//현재 테마를 위한 'dark' 전달
function App(props){
    return(
        <ThemeContext.Provider value="dark">
            <Toolbar />
        </ThemeContext.Provider>
    );
}

//중간위치의 컴포넌트는 테마데이터 전달 필요x
function Toolbar(props){
    return(
        <div>
            <ThemeContext />
        </div>
    );
}

function ThemeButton(props){
    //리액트는 가장 가까운 상위 테마 provider를 찾아서 해당되는 값 사용
    //해당 provider가 없을경우 기본값 사용
    //상위 provider가 있으므로 'dark'적용
    return(
        <ThemeContext.Consumer>
            {value => <Button theme={value}/>}
        </ThemeContext.Consumer>
    );
}

 

사용시 고려할 점 : Component와 Context가 연동되면 재사용성이 저하되므로 데이터를 공유하지 않을 경우는 기본으로 사용 권장

<Page user={user} avatarSize={avatarSize} />	/*1회*/
<PageLayout user={user} avatarSize={avatarSize} />	/*2회*/
<NavigationBar user={user} avatarSize={avatarSize} />	/*3회*/
<Link href={user.permalink}>
     <Avatar user={user} avatarSize={avatarSize} />	/*4회*/
</Link>

//↓
//Context 사용
function Page(props){
    const user = props.user;
    const userLink = (
        <Link href={user.permalink}>
            <Avatar user={user} size={props.avatarSize} />
        </Link>
    );
    return <PageLayout userLink={userLink} />;	//컴포넌트 전달
}
<PageLayout userLink={...} />	//컴포넌트 전달
<NavigationBar userLink={...} />	//컴포넌트 전달


//↓
//하위 컴포넌트의 의존성을 상위 컴포넌트와 분리할 때
function Page(props){
    const user = props.user;
    const topBar = (
        <NavigationBar>
            <Link href={user.permalink}>
                <Avatar user={user} size={props.avatarSize} />
            </Link>
        </NavigationBar>
    );
    const content=<Feed user={user} />
    return (
        <PageLayout 
        	topBar={topBar} 	//하위컴포넌트를 여러개의 변수로 나눠서 전달
        	content={content} 
        />	
    );
}

 

Context API

Context 생성 : 상위레벨에 매칭되는 Provider가 없으면 기본값이 사용됨, 기본값으로 undefined를 넣으면 기본값이 사용되지 않음

const MyContext = React.createContext(기본값);

 

Context.Provider : 하위 컴포넌트들이 해당 Context의 데이터에 접근 가능 

* Consumer component : Context.Provider에 접근하여 데이터를 받는 하위 컴포넌트, Provider의 값이 변경되면 재랜더링

<Mycontext.Provider value={/* some value */}>

주의) Provider 컴포넌트가 재랜더링시마다 모든 하위 consumer 컴포넌트가 재랜더링 됨

//불필요한 재랜더링 발생
function App(props){
  return(
    <MyCountext.Provider value={{ something:'something' }}> //직접 호출
      <Toolbar />
    </MyContext.Provider>
  );
}

//state를 사용하여 불필요한 재랜더링 방지
function App(props){
  const [value, setValue]=useState({something:'something'});
  return(
    <MyCountext.Provider value={value}>
      <Toolbar />
    </MyContext.Provider>
  );
}

 

Class.contextType : provider 하위의 class컴포넌트에서 Context데이터 접근, 현재 사용x

Context.Consumer : Context 의 데이터를 구독하는 Component

<MyContext.Consumer>
  {value => /* 컨텍스트의 값에 따라서 컴포넌트들을 랜더링*/}
</MyContext.Consumer>

 

function as a child : 컴포넌트의 자식으로 함수를 사용하는 방법

// children이라는 prop을 직접 선언
<Profile children={name => <p>이름: {name}</p>} />  

// Profile컴포넌트로 감싸서 children으로 만듦
<Profile>{name => <p>이름: {name}</p>}</Profile>  

 

Context.displayName : 문자열속성

const MyContext = React.createContext(/*some value*/);
MyContext.displayName = 'MyDisplayName';
<MyContext.Provider>  // 개발자도구에 "MyDisplayName.Provider"로 표시됨
<MyContext.Consumer>  // 개발자도구에 "MyDisplayName.Consumer"로 표시됨

 

여러개의 Context

const ThemeContext = React.createContext('light');
const UserContext = React.createContext({
    name:'Guest',
});
class App extends React.Component{
    render(){
        const {signedInUser, theme} = this.props;
        return(
            <ThemeContext.Provider value={theme}>
                <UserContext.Provider value={signedInUser}>
                    <Layout />
                </UserContext.Provider>
            </ThemeContext.Provider>
        )
    }
}

function Layout(){
    return(
        <div>
            <Siderbar />
            <Content />
        </div>
    );
}

function Content(){
    return(
        <ThemeContext.Consumer>
            {theme => (
                <UserContext.Consumer>
                    {user => (
                        <ProfilePage user={user} theme={theme} />
                    )}
                </UserContext.Consumer>
            )}
        </ThemeContext.Consumer>
    )
}

-> useContext : Hook, 함수컴포넌트에서 Context사용이 용이하도록

import { useContext } from "react";

function Mycomponent(props){
    const value = useContext(MyContext);
    return(...)
}

 

(실습) Context를 사용하여 테마 변경 기능 만들기

//ThemeContext.jsx
import React from "react";

const ThemeContext = React.createContext();
ThemeContext.displayName = "ThemeContext";

export default ThemeContext;
//MainContent.jsx
import {useContext} from "react";
import ThemeContext from "./ThemeContext";

function MainContent(props){
    const {theme, toggleTheme} = useContext(ThemeContext);
    return(
        <div
            style={{
                width:"100vw",
                height:"100vh",
                padding: "1.5rem",
                backgroundColor: theme === "light" ? "white" : "black",
                color: theme === "light" ? "black" : "white",
            }}
        >
            <p>안녕하세요</p>
            <button onClick= {toggleTheme}>테마변경</button>
        </div>
    );
}
export default MainContent;
//DarkOrLight.jsx
import {useState, useCallback} from "react";
import ThemeContext from "./ThemeContext";
import MainContent from "./MainContent";

function DarkOrLight(props){
    const [theme, setTheme] = useState("light");
    const toggleTheme = useCallback(()=>{
        if(theme === "light"){
            setTheme("dark");
        }else if(theme === "dark"){
            setTheme("light");
        }
    }, [theme]);
    return (
        <ThemeContext.Provider vale={{theme, toggleTheme}}>
            <MainContent />
        </ThemeContext.Provider>
    );
}
export default DarkOrLight;

 

 

 

반응형

'REACT > [inflearn] 처음만난 리액트' 카테고리의 다른 글

13강. Composition vs Inheritance  (0) 2022.11.30
12강. Lifting State Up  (0) 2022.11.22
11강. Forms  (0) 2022.11.21
10강. List and Keys  (0) 2022.11.11
9강. Conditional Rendering  (0) 2022.11.10