Presentational & Container Components는 Dan Abramov가 React Application에서 관심사 분리 개념(Presentational Component & Container Component)으로 언급하였습니다.
Update from 2019: I wrote this article a long time ago and my views have since evolved. In particular, I don’t suggest splitting your components like this anymore. If you find it natural in your codebase, this pattern can be handy. But I’ve seen it enforced without any necessity and with almost dogmatic fervor far too many times. The main reason I found it useful was because it let me separate complex stateful logic from other aspects of the component. Hooks let me do the same thing without an arbitrary division. This text is left intact for historical reasons but don’t take it too seriously.
-Dan Abramov-
비록 업데이트 된 문서에서는 Container & Presentation Component의 무분별한 사용을 지양하고 Hooks의 적용과 Complex Stateful Logic의 분리 관리에 사용할 것을 제안하고 있습니다.
Dan Abramov는 관심사 분리를 통해 아래의 네 가지 장점을 얻을 수 있다고 명시하였습니다.
Container와 Presentation 구분이 없이 작성된 코드는 아래와 같습니다.
// LoginPage/LoginForm.jsx
import React, { useState, useCallback } from “react”
export const DumbLoginComponent = () => {
const [id, setId] = useState<string>("");
const [pw, setPw] = useState<string>("");
const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);
const handleSubmit = useCallback(() => {
// do something
setIsLoggedIn(true);
},[id, pw]);
return (
!isLoggedIn ? (
<div>
<input value={id} onChange={(e) => setId(e.target.value)}/>
<input value={pw} onChange={(e) => setPw(e.target.value)}/>
<button onClick={() => handleSubmit()}>로그인</button>
</div>
) : null
);
};
위의 코드는 Login Stateful Logic만을 관리하여 문제가 없어 보이지만, Presentation Component에 Stateful Logic이 같이 작성되어 가독성이 떨어지는 코드를 알고 있습니다.
때문에, 가독성과 유지 보수성의 향상을 위해 아래와 같이 코드를 수정합니다.
// Container/LoginContainer.jsx
const LoginContaienr = () => {
const [id, setId] = useState("");
const [pw, setPw] = useState("");
const [isLoggedIn, setIsLoggedIn] = useState(false);
const handleSubmit = useCallback(() => {
// do something
setIsLoggedIn(true);
}, [id, pw]):
return(
<LoginForm {...props} />
)
}
위와 같이 Container Component가 Stateful Logic을 관리하도록 변경합니다.
// Component/LoginForm.jsx
import React from "react";
const LoginForm = (props) => {
const {
isLoggedIn,
id,
setId,
pw,
setPw,
handleSubmit
} = props;
return (
isLoggedIn ? (
<div>
<input value={id} onChange={(e) => setId(e.target.value)}/>
<input value={pw} onChange={(e) => setPw(e.target.value)}/>
<button onClick={() => handleSubmit()}>로그인</button>
</div>
) : null
);
}
LoginForm (Presentaion Component)은 LoginContainer (Container Component)가 전달한 Props를 통해 UI만을 구성합니다.
위와 같은 방법을 통해 Stateful Logic과 UI Component로 구분하였고 문제를 핸들링하는데 장점을 얻을 수 있게 되었습니다.
하지만, 이 같은 Component의 분리가 만능이라고 볼 수는 없습니다. LoginContainer와 LoginForm 간의 종속성이 문제입니다.
이것 또한 Custom Hook을 통해 해결할 수 있습니다.
Hooks/useLogin.jsx
const useLogin = () => {
const [id, setId] = useState("");
const [pw, setPw] = useState("");
const [isLoggedIn, setIsLoggedIn] = useState(false);
const handleSubmit = useCallback(() => {
// do something
setIsLoggedIn(true);
}, [id, pw]):
return {
id,
setId,
pw,
setPw,
handleSubmit
}
}
// Container/LoginContainer.jsx
import { useLogin } from "hooks/useLogin";
const LoginContaienr = () => {
const props = useLogin();
return(
<LoginForm {...props} />
)
}
Container에 Custom Hook을 사용하여 재사용성을 높일 수 있습니다.
Redux 혹은 Recoil State Management Library를 사용함으로써 React의 Prop Drilling을 회피할 수 있게 되었습니다. 그런데 위와 같은 Architecture는 필연적으로 Prop Drilling을 수반하는 것과 같이 보입니다. 현재, 작업중인 소스 코드 상에서 해결책을 찾게 된다면 추가로 업데이트하겠습니다.
Reference :
Dan Abramov Article
Container Component in React
Container & Component
React Hook VS Container Component