게시글

강좌17 - React - 19일 전 등록 / 17일 전 수정

React Hooks! useContext편(React 17)

context api
이 포스팅들은 오래되었습니다. 유튜브에서 최신 리액트 강좌 강좌를 보시는 것을 추천합니다.

안녕하세요. 이번 시간에는 useContext Hook에 대해 알아보겠습니다. context는 리액트 버전에 따라 정말 많이 변경된 API입니다. 한 때는 쓰지 말라고 공식문서에서 말하기까지 했다가, 요즘 React.createContext와 useContext가 나오면서 활발히 사용되고 있습니다. 심지어 Redux를 useContext로 대체하려 하시는 분들도 많습니다.

기존에 컴포넌트 간에 데이터를 전달하려면 props를 이용해야 했습니다. props는 부모 자식 관계에서 데이터를 전달합니다. 따라서 A, B, C 컴포넌트가 각각 부모자식 관계일 때, A에서 C로 데이터를 내려보내주려면 중간 B 컴포넌트를 거쳐야 했죠. A, B, C, D, E 컴포넌트일 때 A에서 E로 데이터를 내려보내주려면 중간 B, C, D 컴포넌트를 거쳐야 합니다. 엄청난 비효율이죠.

따라서 사람들은 이 문제 해결을 위해 Redux를 쓰곤 했습니다. 그러다가 리액트 팀에서 죽어있던 context api를 새로 되살려냈습니다. 

import React, { createContext, useMemo, useState } from 'react';
import Parent from './Parent';

export const UserContext = createContext({
  setLoggedIn: () => {},
  setLoading: () => {},
});
const GrandParent = () => {
  const [loggedIn, setLoggedIn] = useState(false);
  const [loading, setLoading] = useState(false);
  const value = useMemo(() => ({ setLoggedIn, setLoading }), [setLoggedIn, setLoading]);
  return (
    <UserContext.Provider value={value}>
      <Parent />
      <div>{loggedIn ? '로그인' : '로그인안해'}</div>
      <div>{loading ? '로딩중' : '로딩안해'}</div>
    </UserContext.Provider>
  );
};
export default GrandParent;

createContext 내부에 공유하길 원하는 데이터의 초깃값을 넣어두고 value 변수로 묶어주면 됩니다. 이 때 value 객체는 객체이므로 리렌더링의 주범이 되므로 useMemo로 캐싱해두세요. 안 그러면 나중에 이 데이터를 쓰는 모든 컴포넌트가 매번 리렌더링됩니다.

현재 컴포넌트 구조는 GrandParent - Parent - Children입니다. GrandParent에서 선언한 setLoggedIn과 setLoading을 Parent를 거치지 않고 바로 Children으로 보낼 것입니다. 그러기 위해서는 컴포넌트가 UserContext.Provider로 감싸져있어야 합니다. value props에 아까 만든 value를 넣어둡니다.

import React from 'react';
import Children from './Children';

const Parent = () => {
  return <Children />;
};
export default Parent;

Parent는 아무 것도 안 합니다. 과연 Children은 GrandParent의 컨텍스트를 물려받을 수 있을까요?

import React, { useContext } from 'react';
import { UserContext } from './GrandParent';

const Children = () => {
  const { setLoading, setLoggedIn } = useContext(UserContext);
  return (
    <>
      <button onClick={() => setLoading((prev) => !prev)}>로딩토글</button>
      <button onClick={() => setLoggedIn((prev) => !prev)}>로딩토글</button>
    </>
  );
};
export default Children; 

useContext를 불러와서 안에 UserContext를 넣어주면 setLoading과 setLoggedIn을 쓸 수 있습니다. 한 번 버튼을 눌러보세요!

useContext를 쓸 때 주의할 사항은, Provider에 제공한 value가 달라지면 useContext를 쓰고 있는 모든 컴포넌트가 리렌더링 된다는 것입니다. value 안에는 setLoading과 setLoggedIn이 들어있고 앞으로 개수가 더 늘어날 가능성이 높습니다. 그 중 하나라도 바뀌면 객체로 묶여있으므로 전체가 리렌더링되는 것입니다. 따라서 잘못 쓰면 엄청난 렉을 유발할 수 있습니다. 해결 방법은 자주 바뀌는 것들을 별도의 컨텍스트로 묶거나(컨텍스트는 여러 개 쓸 수 있습니다. Provider로만 잘 감싸주세요.), 자식 컴포넌트들을 적절히 분리해서 shouldComponentUpdate, PureComponent, React.memo 등으로 감싸주는 것이 있습니다.

다음 시간에는 Forward ref와 Callback ref에 대해 알아보겠습니다.

조회수:
0
목록
투표로 게시글에 관해 피드백을 해주시면 게시글 수정 시 반영됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright 2016- . 무단 전재 및 재배포 금지. 출처 표기 시 인용 가능.

댓글

아직 댓글이 없습니다.