Context

5o_hyun·2022년 12월 28일
0
post-thumbnail

일반적은 react에서 데이터가 컴포넌트의 props를 통해 부모에서 자식으로 일일히 전달되는 단방향 형태였다.
하지만, 여러 컴포넌트에 걸쳐 자주사용하는 데이터의 경우 코드가 복잡하고 state관리가 안된다.
context는 컴포넌트 트리를 통해 곧바로 컴포넌트에 전달하는구조여서,
일일히 자식컴포넌트까지 전달해줄필요없이 어디서든 데이터를 가져다 쓸수있다.

언제 컨텍스트를 사용해야할까?
여러개의 컴포넌트들이 접근해야하는데이터 (로그인여부, 로그인정보, ui테마, 현재 언어 등..)

기존코드

/*
	앱 안의 모든 버튼이 테마를 알아야 한다면 정보를 자식,,자식,, 일일이 넘겨야한다.
*/
class App extends React.Component {
  render() {
    return <Toolbar theme="dark" />;  // 테마 넘기기..
  }
}

function Toolbar(props) {
  return (
    <div>
      <ThemedButton theme={props.theme} /> // 테마 또 넘기기..
    </div>
  );
}

class ThemedButton extends React.Component {
  render() {
    return <Button theme={this.props.theme} />;// 테마 또또 넘겨서 사용..
  }
}

컨텍스트 사용코드

/*
	context를 사용하면 모든 컴포넌트를 일일이 통하지 않고도
    원하는 값을 컴포넌트 트리 깊숙한 곳까지 보낼 수 있다.
    
    light를 기본값으로 하는 테마 context를 만들어 보자.
*/
const ThemeContext = React.createContext('light');

class App extends React.Component {
  render() {
	// Provider를 이용해 하위 트리에 테마 값을 보내준다.
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

// 이젠 중간에 있는 컴포넌트가 일일이 테마를 넘겨줄 필요가 없다.
function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

class ThemedButton extends React.Component {
  // 현재 선택된 테마 값을 읽기 위해 contextType을 지정합니다.
  // React는 가장 가까이 있는 테마 Provider를 찾아 그 값을 사용할 것입니다.
  // 이 예시에서 현재 선택된 테마는 dark입니다.
  static contextType = ThemeContext;
  render() {
    return <Button theme={this.context} />;
  }
}

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

컨텍스트는 재사용성이 떨어지므로 다른 레벨의 많은 컴포넌트가 특정 데이터를 필요로하는경우만 주로 사용하자.

여러 레벨에 걸쳐 props 넘기는 걸 대체하는데에 컨텍스트보다 컴포넌트합성이 더 간단한 해결책이 될 수도 있다.



예를들어 예를 들어 여러 단계 아래에 있는 LinkAvatar 컴포넌트에게 useravatarSize 라는 props를 전달해야 하는 Page 컴포넌트를 생각해보자.

<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>

실제로 사용되는 곳은 Avatar 컴포넌트 뿐인데 useravatarSize props를 중간단계에 모두 추가해줘야한다.

이럴때는 Avatar 컴포넌트 자체를 넘겨주면 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} />;
}

// 이제 이렇게 쓸 수 있습니다.
<Page user={user} avatarSize={avatarSize} />

<PageLayout userLink={...} />

<NavigationBar userLink={...} />

{props.userLink}

이러한것을 '제어의역전'이라고 하는데 이를 이용하면 props의 수가 줄기때문에 좋다.
하지만 이러한 역전이 항상 옳은것은아니다.
복잡한 로직을 상위컴포넌트로 올리면 이 상위컴포넌트는 더 난해해지고 하위컴포넌트도 필요이상으로 유연해져야하기 때문이다.

이럴땐 하위컴포넌트를 여러개의 변수로 나눠서 전달한다.

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}
    />
  );
}

Context API

Context생성

  1. 컨텍스트를 사용하기 위해 React.createContext() 으로 컨텍스트객체를 생성한다.
const MyContext = React.createContext(기본값);

리액트가 렌더링될때 컨텍스트객체를 구독하는 하위컴포넌트가 나오면, 현재컨텍스트의 값을 가장 가까이에있는 상위레벨의 provider로부터 받아오게된다.
만약 상위레벨에 매칭되는 provider가 없다면 기본값이 사용된다.
기본값으로 undefined를 넣으면 기본값이 사용되지않는다.

Context.Provider

  1. 컨텍스트객체를 만들었다면, 하위컴포넌트들이 해당 데이터를 받을수있도록 설정해줘야한다.

provider는 데이터를 제공해주는 컴포넌트다.
모든 컨텍스트객체는 provider라는 리액트 객체를 갖고있다.
Context.Provider로 하위컴포넌트들을 감싸주면 하위컴포넌트들이 해당컨텍스트의 데이터에 접근할수있게된다.

// provider컴포넌트가 재렌더링될때마다 모든 하위컴포넌트가 재렌더링됨 value props가 계속 재렌더링됨
<MyContext.Provider value={/* value */}>
  
// value를 직접넣지않아, state를 사용하여 불필요한 재렌더링을 막음
function App(props){
  const [value,setValue] = useState({somethig: 'something'});
    
  return (
    <MyContext.Provider value={value}>
      <Toolbar />
    </MyContext.Provider>
  );
}

Context.Consumer

Consumer컴포넌트는 컨텍스트의 데이터를 구독하는 데이터이다.

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

컴포넌트의 자식으로 함수가 올수있는데 function as a child라고한다.

function as a child

컴포넌트의 자식으로 함수를 사용하는 방법

// children이라는 prop을 직접 선언하는 방식
<Profile children={name => <p>이름 : {name}</p>}/>
// Profile컴포넌트로 감싸서 children으로 만드는 방식
<Profile>{name => <p>이름 : {name}</p>}</Profile>

Context.displayName

컨텍스트가 가지는 문자열속성

useContext()

함수컴포넌트에서 컨텍스트를 사용하기위해 컴포넌트를 매번 Consumer컴포넌트를 감싸주는거보다 더 좋은방법이 있다.

useContext()는 React.createContext()로 만든 컨텍스트객체를 인자로받아서 컨텍스트의 값을 리턴한다.

function MyComponent(props){
  const value= useContext(MyContext);
  
  return(
    ...
  )
}
profile
학생 점심 좀 차려

0개의 댓글