반응형
섹션 14. Context
1강. Context란?
- 기존의 일반적인 React 애플리케이션에서는 데이터가 컴포넌트의 props를 통해 부모에서 자식으로 단방향 전달
- 컨텍스트 : 리액트 컴포넌트들 사이에서 데이터를 기존에 props를 통해 전달하는 방식 대신 컴포넌트 트리를 통해 곧바로 컴포넌트로 전달하는 새로운 방식
- 이 방식의 단점은 단방향으로 전달되기 때문에 반복되는 코드가 많아지고 복잡해진다는 것임
- 예를 들어, Root에서 10단계 밑에 있는 자식으로 전달하려면 props를 통해 10번 내려가야 되는 것임
- 이 불편함을 개선하기 위해 등장한 것이 바로 context
- 코드도 매우 깔끔해지고, 데이터를 한 곳에서 관리하기 때문에 디버깅을 하기에도 굉장히 편리함
1) 언제 Context를 사용해야 할까?
- 여러 개의 Component들이 접근해야 하는 데이터
- 예 : 로그인 여부, 로그인 정보, UI테마, 현재 언어 등..
매우 반복적이고 비효율적이기 때문에 번거롭다.
2) Context를 사용하기 전에 고려할 점
- 컨텍스트는 다른 레벨의 많은 컴포넌트가 특정 데이터를 필요로 하는 경우에 주로 사용
- 하지만, 무조건 Context를 사용하는 것이 좋지는 않다.
- 컴포넌트와 컨텍스트가 연동되면 재사용성이 떨어지기 때문 - 다른 레벨의 많은 컴포넌트가 데이터를 필요로 하는 경우가 아니라면 기존에 사용하던 방식대로 props를 통해 데이터를 전달하는 컴포넌트 컴포지션 방법이 더 적합함
- props를 통해 전달하는 방식에서 Avatar에 추가로 정보가 필요하면 같은 방식으로 또 전달해야 하기 때문에 매우 번거롭다.
- 여기서 컨텍스트를 사용하지 않고 이런 문제를 해결하는 방법은 아바타 컴포넌트를 변수에 저장하여 직접 넘겨주는 것
- 유저와 아바타 사이즈가 props로 들어간 아바타 컴포넌트를 userLink라는 변수에 저장한 뒤에 해당 변수를 하위 컴포넌트로 넘기고 있음
- 이렇게 해야 가장 상위 레벨에 있는 페이지 컴포넌트만 아바타 컴포넌트에서 필요로 하는 유저와 아바타 사이즈에 대해 알고 있으면 된다.
- 이 방식은 중간 레벨의 컴포넌트를 통해 전달해야 하는 props를 없애고 코드를 더욱 간결하게 쓸 수 있도록 만들어 줌
- 최상위에 있는 컴포넌트에 좀 더 많은 권한을 부여해 줌
<단점>
데이터가 많아질수록 상위 컴포넌트에 몰리기 때문에 상위 컴포넌트는 점점 더 복잡해지고 하위 컴포넌트는 너무 유연해지게 됨
- Context 사용 적합한 예 : 현재 지역 정보, UI테마, 캐싱된 데이터 등
2강. Context API
1) Context, Provider
- 컨텍스트를 사용하기 위해서 가장 먼저 할 일은 컨텍스트를 생성하는 것
- React.creatContext() 함수 사용
- 하위 컴포넌트들이 해당 컨텍스트의 데이터를 받을 수 있도록 설정해 주어야 함
- Context.Provider 함수 사용
- Provider : 데이터를 제공해 주는 컴포넌트 - 모든 컨텍스트 개체는 Provider라는 React 컴포넌트를 갖고 있음
- Context.Provider 컴포넌트로 하위 컴포넌트들을 감싸주면 모든 하위 컴포넌트들이 해당 컨텍스트의 데이터에 접근할 수 있음
- Provider 컴포넌트에서는 value라는 prop이 있으며, 이것은 Provider 컴포넌트 하위에 있는 컴포넌트들에게 전달됨
- 하위 컴포넌트들이 이 값을 사용하게 되는데, 이 하위 컴포넌트들이 데이터를 소비한다는 뜻에서 컨슈밍 컴포넌트라고 부름
- 컨슈밍 컴포넌트는 컨텍스트 값의 변화를 지켜보다가 값이 변경되면 재렌더링 됨
- 하나의 프로바이더 컴포넌트는 여러 개의 컨슈밍 컴포넌트와 연결될 수 있으며, 여러 개의 프로바이더 컴포넌트는 중첩되어 사용될 수 있음
- 프로바이더 컴포넌트로 감싸진 모든 컨슈밍 컴포넌트는 프로바이더의 value prop이 바뀔 때마다 재렌더링 됨
- 값이 변경되었을 때, 상위 컴포넌트가 업데이트 대상이 아니더라도 하위에 있는 컴포넌트가 context를 사용한다면 하위 컴포넌트에서는 업데이트가 일어남
- 이 때, 값의 변화를 판단하는 기준은 JavaScript 객체의 object.is라는 함수와 같은 방식으로 판단.
<object.is 함수에 대한 자세한 내용>
2) Provider value에서 주의해야 할 사항
- 컨텍스트는 재렌더링 여부를 결정할 때 레퍼런스 정보를 사용하기 때문에 Provider의 부모 컴포넌트가 재렌더링 되었을 경우 의도치 않게 컨슈머 컴포넌트가 재렌더링이 일어날 수 있는 문제가 있음
- 위의 코드는 프로바이더 컴포넌트가 재렌더링 될 때마다 모든 하위 컨슈머 컴포넌트를 재렌더링함.
- value prop을 위한 새로운 객체가 매번 생성되기 때문
- value를 직접 넣는 것이 아닌, 컴포넌트의 state로 옮기고 해당 state의 값을 넣어주어야 함
수정한 이후의 모습
- value를 직접 넣는 것이 아니라 state를 선언하고, state의 값을 프로바이더에 넣음
3) Class.contextType
- Class.contexdtType은 프로바이더 하위에 있는 class 컴포넌트에서 context의 데이터에 접근하기 위해 사용하는 것
- 거의 사용하지 않기 때문에, 이런 방법이 있다는 정도만 알면 될 듯
4) Context.Consumer
- Consumer 컴포넌트 : 컨텍스트의 데이터를 구독하는 컴포넌트
- 클래스 컴포넌트에서는 앞에 나온 Class.contextType을 사용하면 되고, 함수 컴포넌트에서는 Context.Consumer를 사용하여 컨텍스트를 구독
- 컴포넌트의 자식으로 함수가 올 수 있는데, 이것을 function as a child라고 함
- Context.Consumer로 감싸주면 자식으로 들어간 함수가 현재 컨텍스트의 value를 받아서 react node로 리턴하게 됨
- 이때, 함수로 전달되는 value는 프로바이더의 value prop과 동일함 - 상위 컴포넌트에 프로바이더가 없다면 value 파라미터는 creat context를 호출할 때 넣는 기본 값과 동일한 역할을 함
5) function as a child
컴포넌트의 자식으로 함수를 사용하는 방법
React에서는 기본적으로 하위 컴포넌트들을 children이라는 prop으로 전달해 주는데, children으로 컴포넌트 대신 함수를 사용하여 위의 코드와 같이 사용할 수 있음
6) Context.displayName
Context 객체는 displayName이라는 문자열 속성을 가짐
크롬의 리액트 개발자 도구에서는 Context의 Provider나 Consumer를 표시할 때, 이 displayName을 함께 표시함
7) 여러 개의 Context 사용하기
Class.context는 한 번에 하나만 사용 가능
Context.provider를 중첩해서 사용하면 여러 개의 Context 사용 가능
- Theme 컨텍스트와 User 컨텍스트에 대해서
- App 컴포넌트에서는 각 컨텍스트에 대해 2개의 프로바이더를 사용하여 자식 컴포넌트인 레이아웃을 감싸줌
- 실제 컨텍스트의 데이터를 사용하는 컨텐츠 컴포넌트에서는 2개의 컨슈머 컨포넌트를 사용하여 데이터 전달
- -> 이렇게 하면 여러 개의 컨텍스트를 동시에 사용할 수 있음
2개 또는 그 이상의 컨텍스트의 값이 자주 함께 사용될 경우, 모든 값을 한 번에 제공해 주는 별도의 렌더 플랍 컴포넌트를 직접 만드는 것을 고려하는 게 좋음
8) useContext()
- 함수 컴포넌트에서 컨텍스트를 사용하기 위해 컴포넌트를 매번 컨슈머 컴포넌트로 감싸주는 것보다 더 좋은 방법이 있음
- 7강에서 배운 훅을 사용 - useContext 훅은 함수 컴포넌트에서 컨텍스트를 쉽게 사용할 수 있게 해 줌
- react.creatContext 함수 호출로 생성된 컨텍스트 객체를 인자로 받아서 현재 컨텍스트의 값을 리턴함
- useContext 훅을 사용하면 컨텍스트의 값을 다른 방식과 동일하게 컴포넌트 트리상에서 가장 가까운 상위 프로바이더로부터 받아오게 됨
- 컨텍스트의 값이 변경되면 변경된 값과 함께 useContext 훅을 사용하는 컴포넌트가 재렌더링 됨
- useContext 훅을 사용하는 컴포넌트의 렌더링이 꽤 무거운 작업일 경우에는 별도로 최적화 작업을 해줄 필요가 있음
- 파라미터로 context 객체를 넣어줘야 함
Consumer나 Provider를 넣으면 안 됨
3강. (실습) Context를 사용하여 테마 변경 기능 만들기
npm start를 하면
반응형