React inc.hooks의 컨테이너/뷰 패턴
몇 년 전에 우리는 Ember에서 React로 전환했습니다. 처음에 React는 모든 것이 이치에 맞고 아무것도 되지 않는 이상한 미개척 영역처럼 보였습니다.
질문이 생기기 시작했습니다. 구성 요소를 만드는 가장 좋은 방법은 무엇입니까? 언제 만들까요? 가능한 한 재사용 가능하게 유지하는 방법은 무엇입니까?
답을 찾다가 프레젠테이션 및 컨테이너 구성 요소에 대한 Dan Abramov의 이 기사를 발견했습니다. 그것을 읽은 후 나는 그것이 나타내는 아이디어에 즉시 사랑에 빠졌습니다.
컨테이너/뷰 패턴이란 무엇입니까?
컨테이너/뷰 패턴(Presentational/Container, Thick/thin, Smart/Dumb이라고도 함)은 구성 요소를 상태 저장 논리 및 데이터 가져오기를 담당하는 '컨테이너'와 데이터 프레젠테이션을 담당하는 '뷰'로 분할하는 기술입니다. .
이 패턴을 올바르게 사용하면 React 애플리케이션에서 엄청난 확장 옵션을 사용할 수 있습니다. 뷰의 로직을 깨끗하게 유지함으로써 원하는 만큼 뷰를 재사용할 수 있습니다. 그러나 이제 우리의 모든 논리가 컨테이너 안에 포함되어 있으므로 더 빠르고 쉽게 디버깅할 수 있습니다.
다음은 이 패턴을 구현하는 방법에 대한 간단한 예입니다.
뷰 구성 요소를 만드는 것으로 시작하겠습니다. 우리의 경우 사용자의 프로필 사진, 이름, 위치, 성별 및 이메일을 보여주는 간단한 사용자 카드가 될 것입니다.
import style from "./Card.module.css";
const Card = ({ title, location, email, gender, image }) => (
<section className={style.card}>
<img
className={style.cardImage}
src={image}
alt={title}
/>
<div className={style.cardContent}>
<h3 className={style.cardTitle}>{title}</h3>
<span className={style.cardLocation}>{location}</span>
<div className={style.cardContact}>
<span className={style.cardMail}>{`email: ${email}`}</span>
<span className={style.cardGender}>{`gender: ${gender}`}</span>
</div>
</div>
</section>
);
export default Card;
이제 스타일을 추가하여 예쁘게 만들어 보겠습니다.
.card {
display: flex;
align-self: center;
width: fit-content;
background: #ffffff;
box-shadow: 0px 2px 4px rgba(119, 140, 163, 0.06),
0px 4px 6px rgba(119, 140, 163, 0.1);
border-radius: 8px;
padding: 24px;
margin: 0 auto;
}
.cardImage {
height: 80px;
width: 80px;
border-radius: 100px;
}
.cardContent {
font-family: sans-serif;
line-height: 0;
margin-left: 20px;
}
.cardContact {
display: flex;
flex-direction: column;
}
.cardTitle {
font-size: 20px;
color: #112340;
margin-bottom: 20px;
}
.cardLocation {
font-size: 12px;
color: #112340;
margin-bottom: 22px;
opacity: 0.85;
}
.cardMail,
.cardGender {
font-size: 12px;
color: #112340;
margin-top: 15px;
opacity: 0.65;
}
짜잔. 카드가 완성되어 사용할 준비가 되었습니다.
이제 마법이 일어나는 곳입니다. CardContainer라는 새 구성 요소를 만들 것입니다. 이 구성 요소 내부에서 논리가 발생합니다. 임의의 사용자 API에서 사용자를 가져와 카드에 데이터를 표시할 것입니다.
import { useState, useEffect } from "react";
import axios from "axios";
import Card from "@components/Card";
const CardContainer = () => {
const [userData, setUserData] = useState(null);
useEffect(() => {
const fetchData = async () => {
const result = await axios("https://randomuser.me/api/");
const user = result.data.results[0];
setUserData({
gender: user.gender,
email: user.email,
location: `${user.location.city}, ${user.location.country}`,
title: `${user.name.title}. ${user.name.first} ${user.name.last}`,
image: user.picture.thumbnail,
});
};
fetchData();
}, []);
return (
<Card
title={userData?.title || "N/A"}
location={userData?.location || "N/A"}
email={userData?.email || "N/A"}
gender={userData?.gender || "N/A"}
image={userData?.image || ""}
/>
);
};
export default CardContainer;
컨테이너의 모든 논리를 분리하여 볼 수 있듯이 뷰 구성 요소가 깨끗하고 원하는 만큼 재사용할 준비가 되었습니다.
React의 후크 소개
Dan의 블로그에서 후크 소개를 볼 수 있듯이 이와 같은 구성 요소를 패키징할 필요가 없습니다. 후크를 사용하면 내부의 논리를 격리한 다음 필요에 따라 호출할 수 있으므로 컨테이너의 필요성이 서서히 사라지고 있습니다.
그러나 Hooks는 훌륭하지만 모든 문제를 해결하지는 못하므로 이 접근 방식이 여전히 널리 사용되는 이유입니다.
먼저 컨테이너 논리를 useUserData라는 사용자 지정 후크로 이동해 보겠습니다.
import { useState, useEffect } from "react";
import axios from "axios";
export const useUserData = () => {
const [userData, setUserData] = useState(null);
useEffect(() => {
const fetchData = async () => {
const result = await axios("https://randomuser.me/api/");
const user = result.data.results[0];
setUserData({
gender: user.gender,
email: user.email,
location: `${user.location.city}, ${user.location.country}`,
title: `${user.name.title}. ${user.name.first} ${user.name.last}`,
image: user.picture.thumbnail,
});
};
fetchData();
}, []);
return {
gender: userData?.gender || "N/A",
email: userData?.email || "N/A",
location: userData?.location || "N/A",
title: userData?.title || "N/A",
image: userData?.image || "",
};
};
좋아 보인다. 이제 우리의 논리는 컨테이너 대신 후크 안에 있습니다.
하지만 지금 어떻게 혼합합니까?
래퍼를 만들어 볼 수 있습니다.
그걸하자.
import { useUserData } from '@hooks/useUserData';
import Card from "@componets/Card";
const UserCardContainer = () => {
const {
title,
location,
email,
gender,
image,
} = useUserData();
return (
<Card
title={title}
location={location}
email={email}
gender={gender}
image={image}
/>
);
};
export default UserCardContainer;
이제 이것은 또 다른 컨테이너가 아닙니까? 이렇게 하면 논리가 3개의 다른 파일로 분리되는 새로운 임의 분할이 생성됩니다.
나에게 이것은 정말 해키적인 방법이었고 내가 기대했던 것만 큼 깨끗하지 않았습니다.
나는 후크와 컨테이너/뷰 패턴에 대한 아이디어를 좋아했기 때문에 아직 포기할 준비가 되지 않았습니다.
인터넷으로!
온라인에서 파헤친 후 react-hooks-compose라는 라이브러리 형태의 솔루션을 찾았습니다.
이 라이브러리를 사용하면 컨테이너가 필요 없는 사용자 지정 후크로 뷰를 구성할 수 있습니다.
useUserData 후크 및 카드 구성 요소를 구성해 보겠습니다.
import composeHooks from "react-hooks-compose";
import { useUserData } from "@hooks/useUserData";
import Card from "@components/Card";
import CardContainer from "@containers/CardContainer"
// composing card with our hook
const ComposedCard = composeHooks({ useUserData })(Card);
const App = () => {
return (
<div className="app">
<ComposedCard />
<CardContainer />
</div>
);
};
export default App;
드디어 성공 🎉 🎉
개인적으로 나는 어떤 모양이나 형태의 컨테이너/뷰 패턴이 관심사를 분리하고 가능한 한 코드를 재사용할 수 있도록 유지하는 좋은 방법이라고 생각합니다.
Ornio는 이 접근 방식을 좋아하며 더 빠르게 확장하고 구성 요소를 훨씬 더 쉽게 구축하고 테스트할 수 있도록 도와주므로 계속 사용할 것입니다.
이 기사가 도움이 되었기를 바랍니다.
연결:
Dan's original post
react-hooks-compose
code
Reference
이 문제에 관하여(React inc.hooks의 컨테이너/뷰 패턴), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/ornio/container-view-pattern-in-react-inc-hooks-5404텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)