반응형
어떤 컴포넌트가 선언적인가?
어떻게(how) 보여줄까가 아닌 무엇을(what) 보여줄까
아래 글의 도입부에서 명령형 컴포넌트와 선언형 컴포넌트를 다룹니다.
https://tech.kakaopay.com/post/react-query-2/
아래는 명령형 컴포넌트입니다.
import { useState, useEffect } from 'react';
const ImperativeComponent = () => {
const [ isLoading, setIsLoading ] = useState(false);
const [ data, setData ] = useState();
const [ error, setError ] = useState();
useEffect(() => {
!async () => {
try {
setIsLoading(true);
const { json } = await fetch(URL);
setData(json());
setError(undefined);
setIsLoading(false);
} catch(e) {
setData(undefined);
setError(e);
setIsLoading(false);
}
}();
}, []);
if (isLoading) {
return <Spinner/>
}
if (error) {
return <ErrorMessage error={error}/>
}
return <DataView data={data}/>;
}
export default ImperativeComponent;
아래는 선언형 컴포넌트입니다.
import { Suspense } from 'react';
const User = () => {
return (
// UserProfile에서 비동기 데이터를 로딩하고 있는 경우
// Suspense의 fallback을 통해 Spinner를 보여줍니다.
<Suspense fallback={<Spinner/>}>
<UserProfile/>
</Suspense>
);
}
const UserProfile = () => {
// userProfileRepository는 Suspense를 지원하는 "특별한 객체"
const { data } = userProfileRepository();
return (
// 마치 데이터가 "이미 존재하는 것처럼" 사용합니다.
<span> {data.name} / {data.birthDay} </span>
);
}
export default User;
import { Component } from 'react';
class MyCustomErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// 다음 렌더링에서 폴백 UI가 보이도록 상태를 업데이트 합니다.
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 에러 리포팅 서비스에 에러를 기록할 수도 있습니다.
logErrorToMyService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// 폴백 UI를 커스텀하여 렌더링할 수 있습니다.
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
const App = () => {
return (
<MyCustomErrorBoundary>
<MyApp/>
</MyCustomErrorBoundary>
)
}
export default App;
대체 차이가 무엇일까? ~일 때는 ~를 보여주는건 동일한게 아닌가?
곰곰이 생각해보니 아래와 같이 추상화 할 수 있겠네요.
즉 if else를 컴포넌트 내부에서 다루고,
개발자는 그 상황에 보여줄 무엇에만 집중하여 컴포넌트로 개발합니다.
<DeclarativeComponent
case1="case1"
case2="case2"
case3="case3"
>
hello world
</DeclarativeComponent>
즉, 어떤 상황엔 어떤 컴포넌트를 보여줘라!를 프롭스로 명시적으로 나타내는 것입니다!
당연히 명령형 구현이 필요하지만,
API는 선언적이 됩니다!
아래는 위 컴포넌트 명령형 구현입니다.
function randomIntFromInterval(min, max) {
// min and max included
return Math.floor(Math.random() * (max - min + 1) + min);
}
const useLogic = () => randomIntFromInterval(1, 4);
export default function DeclarativeComponent({
case1,
case2,
case3,
children
}) {
const internalLogic = useLogic();
switch (internalLogic) {
case 1: {
return <>{case1}</>;
}
case 2: {
return <>{case2}</>;
}
case 3: {
return <>{case3}</>;
}
default: {
return <>{children}</>;
}
}
}
리액트의 꽃인 props를 통한 컴포넌트 합성의 묘미가 느껴지시죠....?
아래는 제가 프로젝트에서 사용한 방법의 예시입니다
export default function DeclarativeLayout({
tabletView,
children
}) {
const isTablet = useMode();
switch (isTablet) {
case true: {
return <TabletLayout>{tabletView}</TabletLayout>;
}
default: {
return <MobileLayout>{children}</MobileLayout>;
}
}
}
사실 레이아웃도 같이 주입해주는게 좋습니다. 그냥 이해를 위해 저렇게 적어뒀다 생각해주세요
참고 :
https://itchallenger.tistory.com/485
반응형
'FrontEnd' 카테고리의 다른 글
리액트 국제화(i18n, internationalization) translation key 설계 (0) | 2022.07.21 |
---|---|
리액트를 직접 만들며 알아보는 렌더/커밋/조정 알고리즘 (1) | 2022.07.19 |
Valtio의 프록시 상태관리가 어떻게 동작할까? (React Part) (1) | 2022.07.18 |
Valtio의 프록시 상태관리가 어떻게 동작할까? (Vanila Part) (0) | 2022.07.18 |
언제 리코일을 사용하는게 좋을까? (6) | 2022.07.17 |