React 리액트 테마 변경 기능
- 메인 컨텐츠 컨포넌트는 씸 컨텍스트로부터 현재 설정된 테마 값을 받아와 실제 화면의 컨텐츠를 렌더링하는 역할
- 테마 변경 버튼을 누를 경우 씸 컨텍스트로부터 받은 roggleTheme함수를 호출하여 씸 컨텍스트의 값을 변경하는 역할도 함
- Theme 컨텍스트의 값을 가져오기 위해 Theme 컨텍스트.consumer 컴포넌트를 사용하는 방법 대신에 use-context-hook를 사용
- darkOrlight 컴포넌트는 방금 전에 만든 메인 컨텐츠 컴포넌트를 자식으로 갖고 있는데 이는 ThemeContext.Provider로 감싸서 theme-context의 값을 하위 컴포넌트들이 사용할 수 있도록 해줌
- SimContext.Provider로 감싸주지 않으면 하위 컴포넌트들이 SimContext의 값을 가져올 수 없음
- SimContext의 값으로 들어가는 sim과 toggleSim 함수는 자체적으로 관리하고 있음
- 처음 화면은 흰색 배경에 검은 글씨인 라이트 테마로 나타나지만 테마 변경 버튼을 누르면 검은색 배경에 흰색 글씨를 가진 다크 테마로 변경
- 기존의 일반적인 React 애플리케이션에서는 데이터가 컴포넌트의 props를 통해 부모에서 자식으로 단방향 전달
- 여러 컴포넌트에 걸쳐 자주 사용되는 데이터의 경우 기존 방식을 사용하면 코드가 복잡해지고 사용하기에 불편함이 존재
🎞️ Context
- 리액트 컴포넌트들 사이에서 데이터를 기존의 props을 통해 전달하는 방식 대신 컴포넌트 트리를 통해 곧바로 컴포넌트로 전달하는 새로운 방식 제공
- 어떤 컴포넌트든지 데이터에 쉽게 접근 가능
- props를 통하여 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달하는 일반적인 방식
- 단점은 여러 컴퍼넌트에 걸쳐서 자주 사용되는 데이터을 전달하려면 코드가 많이 생기고 지저분해진다는 것
- 컨텍스트를 사용하면 일일이 props로 전달할 필요 없이 데이터를 필요로 하는 컴포넌트에 곧바로 데이터를 전달할 수 있음
- 코드도 깔끔해지고 데이터를 한 곳에서 관리하기 때문에 디버깅을 하기에도 굉장히 유리
🎞️ Context 사용
- 여러 컴포넌트에서 자주 필요로 하는 데이터로는 사용자의 로그인 여부, 로그인 정보, ui 테마, 현재 선택된 언어 등이 있음
- 이러한 데이터들을 기존 방식대로 컴포넌트의 props를 통해 넘겨주게 되면 자식 컴포넌트의 자식 컴포넌트까지 계속헤서 내려갈 수밖에 없음
function App(props) {
return <Toolbar theme="dark"/>;
}
function Toolbar(props) {
// 이 Toolbar 컴포넌트는 ThemedButton에 theme를 넘겨주기 위해서 'theme' prop을 가져야만 합니다.
// 현재 테마를 알아야 하는 모든 버튼에 대해서 props로 전달하는 것은 굉장히 비효울적입니다
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
function ThmemedButton(props) {
return <Button theme={props.theme} />;
}
- 코드는 현재 선택된 테마를 기존 방식을 사용하여 컴포넌트의 props로 전달하는 예제
- 가장 상위 컴포넌트인 app 컴포넌트에서는 toolbar 컴포넌트를 사용하고 있음
- theme이라는 이름의 props으로 현재 테마인 dark를 넘김
- Toolbar 컴포넌트에서는 Themed Button 컴포넌트를 사용하는데 Themed Button 컴포넌트에서 현재 테마를 필요로 함
- props으로 받은 Themed을 하위 컴포넌트인 Themeed Button 컴포넌트에 전달
- 최종적으로 Themed Button 컴포넌트에서는 props.theme으로 데이터에 접근하여 어두운 테마를 입히게 됨
- props를 통해서 데이터를 전달하는 기존 방식은 실제 데이터를 필요로 하는 컴포넌트까지의 깊이가 깊어질수록 복잡해짐
- 반복적인 코드를 계속해서 작성해주어야 하기 때문에 비효율적이고 직관적이지도 않음
- 컨택스를 사용하면 이런 방식을 깔끔하게 개선할 수 있음
// 컨텍스트는 데이터를 매번 컴포넌트를 통해 전달할 필요 없이 컴포넌트 트리로 곧바로 전달하게 해줍니다
// 여기에서는 현재 테마를 위한 컨텍스트를 생성하며, 기본값은 'light'입니다
const ThemeContext = React.createContext('light');
// Provider를 사용하여 하위 컴포넌트들에게 현제 테마 데이터를 전달합니다
// 모든 하위 컴포넌트들은 컴포넌트 트리 하단에 얼마나 깊이 있는지에 관계없이 데이터를 읽을 수 있음
// 여기에서는 현재 테마값으로 'dark'를 전달하고 있습니다
function App(props) {
return (
<ThemeContext.Provider value="dark">
<Toolbar/>
</ThemeContext.Provider>
);
}
// 이제 중간에 위치한 컴포넌트는 테마 데이터를 하위 컴포넌트로 전달할 필요가 있음
function Toolbar(props) {
return (
<div>
<ThemeButton />
</div>
);
}
function ThemeButton(props){
// 리액트는 가장 가까운 상위 테마 Provider를 찾아서 해당되는 갑을 사용합니다
// 만약 해당하는 Provider가 없을 경우 기본값('light')을 사용합니다
// 여기에서는 상위 Provider가 있기 때문에 현재 테마의 값은 'dark'가 됨
return (
<ThemeContext.Consumer>
{value => <Button theme={value} /}
</ThemeContext.Consumer>
);
}
- 컨텍스트를 사용한 코드는 전체적으로 간결하고 깔끔하며 직관적으로 바뀐 것을 알 수 있음
- 여러 컴포넌트에서 계속해서 접근이 일어날 수 있는 데이터들이 있는 경우에는 컨텍스트를 사용하는 것이 좋음
🎞️ Context 고려할 점
- 다른 레벨의 많은 컴포넌트가 특정 데이터를 필요로 하는 경우에는 주로 사용함
- 컴포넌트와 Context가 연동되면 재사용성이 떨어지기 때문임
- 다른 레벨의 많은 컴포넌트가 데이터를 필요로 하는 경우가 아니라면 기존에 사용하던 방식대로 props를 통해 데이터를 전달하는 컴포넌트 컴포지션 방법이 더 적합
// Page컴포넌트는 PageLayout컴포넌트를 렌더링
<Page user={user} avatarSize={avatarSize} />
// PageLayout컴포넌트는 NavigationBar컴포넌트를 렌더링
<PageLayout user={user} avaterSize={avatarSize} />
// NavigationBar컴포넌트는 Link컴포넌트를 렌더링
<NavigationBar user={user} avatarSize={avatarSize} />
// Link컴포넌트는 Avatar컴포넌트를 렌더링
<Link href={user.permalink}>
<Avatar user={user} Size={avatarSize} />
<Link>
- 코드는 사용자 정보와 아바타 사이지를 몇 단계를 걸쳐서 하위 컴포넌트인 링크와 아바타를 전달하는 페이지 컴포넌트가 있음
- 가장 하위 레벨에 위치한 아바타 컴포넌트가 유저와 아바타 사이즈를 필요로 하기 때문에 이를 위해 여러 단계를 걸쳐 서 props를 통해 유저와 아바타 사이즈를 전달해주고 있음
- 아바타 컴포넌트에 추가적인 데이터가 필요해지면 해당 데이터도 추가로 여러 단계에 걸쳐서 넘겨주어야 하기 때문에 굉장히 번거로움
- 컨텍스트를 사용하지 않고 이러한 문제를 해결할 수 있는 한가지 방법은 아바타 컴포넌트를 변수에 저장하여 직접 넘겨주는 것
- 중간 단계에 있는 컴포넌트들은 유저와 아바타 사이즈에 대해 전혀 몰라도 됨
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={...} />
- userlink라는 변수에 저장한 뒤에 해당 변수를 하위 컴포넌트로 넘기고 있음
- 가장 상위 레벨에 있는 페이지 컴포넌트만 아바타 컴포넌트에서 필요로 하는 유저와 아바타 사이즈에 대해 알고 있으면 됨
- 중간 레벨의 컴포넌트를 통해 전달해야 하는 props를 없애고 코드를 더욱 간결하게 만들어줌
- 최상위에 있는 컴포넌트에 좀 더 많은 권한을 부여
- 데이터가 많아질 수록 상위 컴포넌트에 몰리기 때문에 상위 컴포넌트는 점점 복잡해지고 하위 컴포넌트는 너무 유연해지게 됨
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는 해당 데이터와 데이터의 변경 사항을 모두 하위 컴포넌트들에게 브로드캐스트 해주기 때문임
🎞️ Context API
- 컨택스트를 사용하기 위해서 가장 먼저 해야 하는 일은 컨택스트를 생성하는 것
- 컨텍스트를 생성하기 위해서 react.createContext 함수 사용
- 만약 상위 레벨에 매칭되는 Provider가 없다면 기본값이 사용 됨
- 기본값으로 undefined를 넣으면 기본값이 사용되지 않음
공부한 자료
https://www.inflearn.com/course/%EC%B2%98%EC%9D%8C-%EB%A7%8C%EB%82%9C-%EB%A6%AC%EC%95%A1%ED%8A% B8/dashboard
[지금 무료] 처음 만난 리액트(React) 강의 | Inje Lee (소플) - 인프런
Inje Lee (소플) | 자바스크립트와 CSS 기초 문법과 함께 리액트의 기초를 탄탄하게 다질 수 있습니다., 깔끔한 강의자료, 꼼꼼한 설명으로쉽게 배우는 리액트 강의입니다. 👨🏫 리액트의 세계로
www.inflearn.com
'Front > React.js' 카테고리의 다른 글
[React] 리액트 styled-components (0) | 2024.08.20 |
---|---|
[React] 리액트 스타일링 (Styling) (0) | 2024.08.19 |
[React] Card 컴포넌트 제작 (Composition VS Inheritance) (0) | 2024.08.17 |
[React] 섭씨 온도와 화씨 온도 표시하기 (Lifting State Up) (0) | 2024.08.17 |
[React] 사용자 정보 입력 받기 (Forms) (0) | 2024.08.15 |