Presentational & Container Components (1)

seohyun Kang·2022년 5월 25일
0

React

목록 보기
1/9

About Presentation & Container Components

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의 분리 관리에 사용할 것을 제안하고 있습니다.

What is the Presentation Component?

  • Are concerned with how things look.
  • May contain both presentational and container components** inside, and usually have some DOM markup and styles of their own.
  • Often allow containment via this.props.children.
  • Have no dependencies on the rest of the app, such as Flux actions or stores.
  • Don’t specify how the data is loaded or mutated.
  • Receive data and callbacks exclusively via props.
  • Rarely have their own state (when they do, it’s UI state rather than data).
  • Are written as functional components unless they need state, lifecycle hooks, or performance optimizations.
  • Examples: Page, Sidebar, Story, UserInfo, List.

What is the Container Component?

  • Are concerned with how things work.
  • May contain both presentational and container components** inside but usually don’t have any DOM markup of their own except for some wrapping divs, and never have any styles.
  • Provide the data and behavior to presentational or other container components.
  • Call Flux actions and provide these as callbacks to the presentational components.
  • Are often stateful, as they tend to serve as data sources.
  • Are usually generated using higher order components such as connect() from React Redux, createContainer() from Relay, or Container.create() from Flux Utils, rather than written by hand.
  • Examples: UserPage, FollowersSidebar, StoryContainer, FollowedUserList.

Benefits

Dan Abramov는 관심사 분리를 통해 아래의 네 가지 장점을 얻을 수 있다고 명시하였습니다.

  • Better separation of concerns. You understand your app and your UI better by writing components this way.
  • Better reusability. You can use the same presentational component with completely different state sources, and turn those into separate container components that can be further reused.
  • Presentational components are essentially your app’s “palette”. You can put them on a single page and let the designer tweak all their variations without touching the app’s logic. You can run screenshot regression tests on that page.
  • This forces you to extract “layout components” such as Sidebar, Page, ContextMenu and use this.props.children instead of duplicating the same markup and layout in several container components.

Examples

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을 사용하여 재사용성을 높일 수 있습니다.

Problem

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

0개의 댓글