FrontEnd/React

Context

Context란?

 

기존에는 컴포넌트의 props를 통하여 단 방향 데이터 전달되었다.

여러 컴포넌트를 거쳐 자주 사용되는 데이터의 경우 코드의 복잡성과 불편함을 야기한다.

이를 개선하고자 Context를 사용한다.

Component Tree를 통해 직접적으로 전달하여 위 문제점을 해결

React Context

 

언제 Context를 사용해야 할까?

여러 개의 Component들이 접근해야 하는 데이터

ex) 로그인 여부, 로그인 정보, UI테마, 현재 언어 등...

 

Context 적용 전

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

function Toolbar(props) {
    // 이 Toolbar 컴포넌트는 ThemedButton에 theme를 넘겨주기 위해서 'theme' prop을 가져야만 합니다.
    // 현재 테마를 알아야 하는 모든 버튼에 대해서 props로 전달하는 것은 굉장히 비효율적입니다.
    return (
      <div>
        <ThemedButton theme={props.theme} />
      </div>
    );
  }
  
  function ThemedButton(props) {
    return <Button theme={props.theme} />
  }

 

Context 적용 후

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

function App(props) {
    return (
        <ThemeContext.Provider value="dark">
            <Toolbar />
        </ThemeContext.Provider>
    );
}

// 이제 중간에 위치한 컴포넌트는 테마 데이털르 하위 컴포넌트로 전달할 필요가 없습니다.
function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemeButton(props) {
    // 리액트는 가장 가까운 상위 테마 Provider를 찾아서 해당되는 값을 사용합니다.
    // 만약 해당되는 Provider가 없을 경우 기본값(여기에서는 'light')을 사용합니다.
    // 여기에서는 상위 Provider가 있기 때문에 현재 테마의 값은 'dark'가 됩니다.

    return (
        <ThemeContext.Consumer>
            {value => <Button theme={value} />}
        </ThemeContext.Consumer>
    );
}

 

 

Context를 사용하기 전에 고려할 점

Context의 주 용도는 다양한 레벨에 네스팅된 많은 컴포넌트에게 데이터를 전달하는 것

Context 사용시 재사용하기 어려워 지기에 필요시에만 사용

여러 레벨에 걸쳐 props를 넘기는 것을 대체하기에 컴포넌트 합성이 더 간단할 수 도 있음

// Page컴포넌트는 PageLayout컴포넌트를 렌더링
<Page user={user} avatarSize={avatarSize} />

// PageLayout컴포넌트는 NavigationBar 컴포넌트를 렌더링
<PageLayout user={user} avatarSize={avatarSize} />

// NavigationBar 컴포넌트는 Link컴포넌트를 렌더링
<NavigationBar user={user} avatarSize={avatarSize} />

// Link컴포넌트는 Avatar컴포넌트를 렌더링
<Link href={user.permalink}>
    <Avatar user={user} size={avatarSize} />
</Link>

 

컴포넌트 합성 예제

function Page(props) {
    const user = props.user;

    const userLink = (
        <Link href={user.permalink}>
            <Avatar user={user} size={props.avatarSize} />
        </Link>
    );

    // Page 컴포넌트는 PageLayout 컴포넌트를 렌더링
    // 이때 props로 userLink를 함께 전달함
    return <PageLayout userLink={userLink} />;
}

// PageLayout 컴포넌트는 NavigationBar 컴포넌트를 렌더링
<PageLayout userLink={...} />

// NavigationBar 컴포넌트는 props로 전달받은 userLink element를 리턴
<NavigationBar userLink={...} />

 

 

컴포넌트 합성 예제2

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

Provider 사용

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

/*
Provider Component로 감싸진 모든 Consuming Component는
Provider의 value prop이 바뀔 때 마다 재 렌더링 된다.
값이 변경되었을때 상위 컴포넌트가 변경 대상이 아니더라도
하위에 있는 컴포넌트가 Context를 사용한다면
하위 컴포넌트에서는 업데이트가 일어난다.
값의 변화 기준은 Reference의 MDN참조
*/

 

Provider value에서 주의 사항

// Provider 컴포넌트가 재렌더링될 때마다
// 모든 하위 consumer 컴포넌트가 재렌더링 됨
function App(props) {
    return (
        <MyContext.Provider value={{ something : 'something'}}>
            <ToolBar />
        </MyContext.Provider>
    );
}

// state를 사용하여 불필요한 재렌더링을 막음
function App(props) {
    const [value, setValue] = useState({ something : 'something'});

    return (
        <MyContext.Provider value={value}>
            <ToolBar />
        </MyContext.Provider>
    );
}

 

Class.context

provider하위에 있는 클래스 컴포넌트에서 context 데이터에 접근하기 위해 사용

class MyClass extends React.Component {
    componentDidMount() {
        let value = this.context;
        /* MyContext의 값을 이용하여 원하는 작업을 수행 가능 */
    }
    componentDidUpdate() {
        let value = this.context;
        /* ... */
    }
    componentWillUnmount() {
        let value = this.context;
        /* ... */
    }
    render() {
        let value = this.context;
        /* MyContext의 값에 따라서 컴포넌트들을 렌더링 */
    }
}

MyClass.contextType = MyContext;

 

Context.Consumer

// Component의 자식으로 함수가 올 수 있는데 이를 function as a child라 한다.
<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';

// 개발자 도구에 "MyDisplayName.Provider"로 표시됨
<MyContext.Provider />

// 개발자 도구에 "MyDisplay.Consumer"로 표시됨
<MyContext.Consumer />

 

여러 개의 Context 사용하기

Context.Provider의 중첩

// 테마를 위한 컨텍스트
const ThemeContext = React.createContext('light');

// 로그인한 유저 정보를 담는 UserContext
const UserContext = React.createContext({
  name: 'Guest',
});

class App extends React.Component {
  render() {
    const {signedInUser, theme} = this.props;

    // context 초기값을 제공하는 App 컴포넌트
    return (
      <ThemeContext.Provider value={theme}>
        <UserContext.Provider value={signedInUser}>
          <Layout />
        </UserContext.Provider>
      </ThemeContext.Provider>
    );
  }
}

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

// 여러 context의 값을 받는 컴포넌트
function Content() {
  return (
    <ThemeContext.Consumer>
      {theme => (
        <UserContext.Consumer>
          {user => (
            <ProfilePage user={user} theme={theme} />
          )}
        </UserContext.Consumer>
      )}
    </ThemeContext.Consumer>
  );
}

 

useContext()

useContext() Hook을 사용한 예시

import { useContext } from "react";

function MyComponent(props) {
    const value = useContext(MyComponent);

    return (
        ...
    )
}

// 올바른 사용법
useContext(MyContext);

// 잘못된 사용법
useContext(MyContext.Consumer);
useContext(MyContext.Provider);

Reference

 

인프런 - 처음 만난 리액트(React)

 

[지금 무료] 처음 만난 리액트(React) | Inje Lee (소플) - 인프런

Inje Lee (소플) | 자바스크립트와 CSS 기초 문법과 함께 리액트의 기초를 탄탄하게 다질 수 있습니다., 깔끔한 강의자료, 꼼꼼한 설명으로쉽게 배우는 리액트 강의입니다. 👨‍🏫 리액트의 세계로

www.inflearn.com

 

https://ko.legacy.reactjs.org/docs/context.html#gatsby-focus-wrapper

 

Context – React

A JavaScript library for building user interfaces

ko.legacy.reactjs.org

 

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is

 

Object.is() - JavaScript | MDN

The Object.is() static method determines whether two values are the same value.

developer.mozilla.org

 

'FrontEnd > React' 카테고리의 다른 글

Mini Project  (0) 2024.05.21
Styling  (0) 2024.05.08
Composition vs Inheritacne  (0) 2024.04.29
Lifting State Up  (0) 2024.04.24
Forms  (0) 2024.04.23