Context[React]

SnowCat·2023년 3월 13일
0
post-thumbnail

Context?

  • 다른 라이브러리를 사용하지 않으면 데이터 전달은 위에서 아래 방향으로 prop을 통해 전달됨
  • 그런데 유저 데이터, 테마 설정과 같이 여러 컴포넌트들에서 산발적으로 사용되는 prop의 경우 prop사용이 번거로워질 수 있음
  • context는 트리 단계마다 prop사용 없이 값을 공유하게 해주는 방법으로 사용할 수 있음

Context를 사용할 때

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

// theme 변수를 사용하지 않음에도 불구하고 prop 전달을 위해 theme 변수를 알아야 함
function Toolbar(props) {
  return (
    <div>
      <ThemedButton theme={props.theme} />
    </div>
  );
}

function ThemedButton(props) {
  return <Button theme={props.theme} />;
}
  • 예시와 같이 모든 컴포넌트를 통하지 않고 변수를 전달하고자 할 때 context를 사용할 수 있음
import { createContext, useContext } from 'react';

const ThemeContext = createContext('light');

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

function Toolbar({}) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const color = useContext(ThemeContext);
  render() {
    return <Button theme={color} />;
  }
}

context의 대안

  • context의 주된 용도는 다양한 레벨에 네스팅 된 많은 컴포넌트에 데이터를 전달하는 것
  • 너무 많은 context를 사용하면 재사용에 어려움이 있으니 주의
  • 대신 컴포넌트 자체를 넘겨주는 방법이 대안이 될 수 있음
// 기존 코드로 Page부터 avatar까지 prop이 넘겨지는 상황
<Page user={user} avatarSize={avatarSize} />

<PageLayout user={user} avatarSize={avatarSize} />

<NavigationBar user={user} avatarSize={avatarSize} />

<Link href={user.permalink}>
  <Avatar user={user} size={avatarSize} />
</Link>

// Link부터를 최상단 Page코드로 분리 가능
function Page(props) {
  const user = props.user;
  const userLink = (
    <Link href={user.permalink}>
      <Avatar user={user} size={props.avatarSize} />
    </Link>
  );
  return <PageLayout userLink={userLink} />;
}

// 이제 다른 컴포넌트들은 user, avatarSize에 대한 prop을 몰라도 됨
<Page user={user} avatarSize={avatarSize} />
<PageLayout userLink={...} />
<NavigationBar userLink={...} />
{props.userLink}

// prop에 컴포넌트를 전달하는 방식을 통한 추가 리팩토링
function Page(props) {
  const user = props.user;
  const content = <Feed user={user} />;
  const topBar = (
    <NavigationBar>
      <Link href={user.permalink}>
        <Avatar user={user} size={props.avatarSize} />
      </Link>
    </NavigationBar>
  );
  return (
    <PageLayout
      topBar={topBar}
      content={content}
    />
  );
}
  • Prop의 수가 줄고, 최상위 코드의 제어력이 커지기 떄문에 코드가 깔끔해지는 경우가 많음
  • 하지만 컴포넌트에 복잡한 로직이 포함되어있을 경우 상위 컴포넌트는 더 난해해지고 하위 컴포넌트는 필요 이상으로 유연해저야하는 문제점이 발생함

API

React.createContext

const MyContext = React.createContext(defaultValue);
  • Context 객체를 생성하는 메서드
  • 객체를 구독하는 컴포넌트를 렌더링할때는 상위 트리에서 가장 가까이 있는 Provider부터 값을 읽게 됨
  • defaultValue는 트리 안에서 적절한 Provider가 없을 때에만 사용되는 값

Context.Provider

<MyContext.Provider value={/* any value */}>
  • Conext를 구독하는 모든 컴포넌트들에거 context의 변화를 알라는 컴포넌트
  • value prop을 받아 값을 하위에 있는 컴포넌트들에게 전달함
  • 여러개의 provide를 중첩하는 것도 가능하며, 이 때는 하위 Provider의 값이 우선적으로 사용됨
  • Context를 구독하는 모든 컴포넌트는 provider의 value prop이 바뀔 때마다 다시 렌더링 됨
    상위 컴포넌트가 업데이트를 하지 않더라도 consumer는 업데이트되며, 이로인해 불필요한 컴포넌트 업데이트가 생길 수 있음
    컴포넌트 업데이트를 방지하려면 context에서 구독하는 값을 부모 state로 끌어올려야 함
// Provider의 부모가 렌더링 될때마다 레퍼런스가 변경되기 때문에 자식 요소가 불필요하게 업데이트 됨
function App() {
  return (
    <MyContext.Provider value={{something: 'something'}}>
      <Toolbar />
    </MyContext.Provider>
  );
}

// state를 사용해 업데이트 방지
function App() {
  const [something, setSomething] = useState("something");
  
  return (
    <MyContext.Provider value={something}>
      <Toolbar />
    </MyContext.Provider>
  );
}

useContext

const value = useContext(MyContext);

//example
function Button() {
  const theme = useContext(ThemeContext);
  return <button className={theme} />;
}
  • context 객체의 값을 받아오는 hook
  • 이 때 값은 트리에서 가장 가까운 위치의 Provider의 context 값이 됨
  • 가장 가까운 위치의 provider가 갱신될 때 hook은 context value를 사용해 다시 렌더링하게 됨

Context.Consumer

<MyContext.Consumer>
  {value => /* rendering */}
</MyContext.Consumer>

//example
function Button() {
  return (
    <ThemeContext.Consumer>
      {theme => (
        <button className={theme} />
      )}
    </ThemeContext.Consumer>
  );
}
  • 함수 컴포넌트에서 context를 받아올 수 있는 다른 방법
  • 새로운 코드 작성시에는 useContext 사용 권장

출처:
https://ko.reactjs.org/docs/context.html
https://ko.reactjs.org/docs/hooks-reference.html#usecontext
https://beta.reactjs.org/reference/react/createContext#consumer

profile
냐아아아아아아아아앙

0개의 댓글