출처: https://bumcrush.tistory.com/182 [맑음때때로 여름]

상세 컨텐츠

본문 제목

presentational component와 container component

React

by 장동균 2020. 8. 19. 13:45

본문

프레젠테이셔널 컴포넌트란 주로 상태 관리가 이루어지지 않고 그저 props를 받아와서 화면에 UI를 보여주기만 하는 것이다.

 

이와 달리 컨테이너 컴포넌트는 리덕스와 연동되어 있는 컴포넌트로 리덕스로부터 상태를 받아오기도 액션을 디스패치하기도 한다.

 

이러한 패턴은 필수는 아니지만 코드의 재사용성과 관심사의 분리를 통해 UI에 집중하고 싶다면 이런 방식이 좋고 많이 쓰인다.

 

컨테이너 컴포넌트(src/containers) - redux와 연관되어 있는 것들

프레젠테이셔널 컴포넌트(src/components) - UI와 관련되어 사람들(user)에게 보여지는 것들

 

하나의 코드를 예로 들겠다.

 

import React from "react";
import { withRouter } from "react-router-dom";

const LoginPage = (props) => {

  const [Email, setEmail] = useState("");
  const [Password, setPassword] = useState("");

  const onEmailHandler = (event) => {
    setEmail(event.currentTarget.value);
  };
  const onPasswordHandler = (event) => {
    setPassword(event.currentTarget.value);
  };
  const onSubmitHandler = (event) => {
    event.preventDefault();

    let body = {
      email: Email,
      password: Password,
    };

    dispatch(loginUser(body)).then((response) => {
      if (response.payload.loginSuccess) {
        props.history.push("/");
      } else {
        alert("Error");
      }
    },[]);
  };

  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        width: "100%",
        height: "100vh",
      }}
    >
      <form
        style={{
          display: "flex",
          flexDirection: "column",
        }}
        onSubmit={onSubmitHandler}
      >
        <label>Email</label>
        <input type="email" value={Email} onChange={onEmailHandler} />
        <label>Password</label>
        <input type="password" value={Password} onChange={onPasswordHandler} />
        <br />
        <button type="submit">Login</button>
      </form>
    </div>
  );
};

export default withRouter(LoginPage);

(고치기 전 파일을 남겨놓지 않아서 맨 위에 import부분을 알아서 몇개 더 추가해야한다.)

이 파일을 보면 redux와 연관되어 있는 것들과 UI와 연관되어 있는 것들이 함께 구성되어 있다. 나는 이 코드를 component부분과 container부분으로 나눠야겠다는 생각을 했고 결과는 다음과 같다

 

src/components/LoginPage.js

import React from "react";
import { withRouter } from "react-router-dom";

const LoginPage = ({
  onSubmitHandler,
  Email,
  onEmailHandler,
  Password,
  onPasswordHandler,
}) => (
  <div
    style={{
      display: "flex",
      justifyContent: "center",
      alignItems: "center",
      width: "100%",
      height: "100vh",
    }}
  >
    <form
      style={{
        display: "flex",
        flexDirection: "column",
      }}
      onSubmit={onSubmitHandler}
    >
      <label>Email</label>
      <input type="email" value={Email} onChange={onEmailHandler} />
      <label>Password</label>
      <input type="password" value={Password} onChange={onPasswordHandler} />
      <br />
      <button type="submit">Login</button>
    </form>
  </div>
);

export default withRouter(LoginPage);

 

 

src/containers/LoginPageContainer.js

 

import React,{useState} from 'react';
import LoginPage from '../components/views/LoginPage/LoginPage';
import {useDispatch} from 'react-redux';
import { loginUser } from "../_actions/user_action";

const LoginPageContainer=(props)=>{
  const dispatch = useDispatch();

  const [Email, setEmail] = useState("");
  const [Password, setPassword] = useState("");

  const onEmailHandler=e=>{
    setEmail(e.currentTarget.value);
  }
  const onPasswordHandler=e=>{
      setPassword(e.currentTarget.value);
  }

  const onSubmitHandler = (event) => {
    event.preventDefault();

    let body = {
      email: Email,
      password: Password
    };

    dispatch(loginUser(body)).then((response) => {
      if (response.payload.loginSuccess) {
        props.history.push("/");
      } else {
        alert("Error");
      }
    }, []);
  };
  return (
    <LoginPage
      Email={Email}
      Password={Password}
      onEmailHandler={onEmailHandler}
      onPasswordHandler={onPasswordHandler}
      onSubmitHandler={onSubmitHandler}
    />
  );
};


export default LoginPageContainer;

이런 식으로 분리를 하였다.

 

가장 큰 난관이었던 부분은 실질적으로 input이 component에서 발생하는데 변할 때마다 작동하는 함수들을 container에 작성해버리면 제대로 동작하지 않을 것이라 예상했다. 하지만, 결과는 그렇지 않았는데 

https://stackoverflow.com/questions/63464816/i-want-to-split-this-component-to-component-which-is-rendered-by-container-but/63465270?noredirect=1#comment112237538_63465270

 

I want to split this component to component which is rendered by container. But, I don't know how to change please help me

import React, { useState } from "react"; import { useDispatch } from "react-redux"; import { loginUser } from "../../../_actions/user_action"; import { withRouter } fr...

stackoverflow.com

이곳에 질문을 해서 알 수 있었다. 저런 식으로 작성해도 부모/자식 component에서 공유가 되나보다.

 

물론 이러한 분류법이 가장 많이 쓰이기는 하나, 다른 분류법들도 존재하는데

https://medium.com/@seungha_kim_IT/presentational-and-container-components-%EB%B2%88%EC%97%AD-1b1fb2e36afb

 

Presentational and Container Components (번역)

(원문: https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0)

medium.com

이곳에 잘 정리가 되어있다. 궁금할 때 다시 들어가서 천천히 읽어보면 좋을 것 같다.

 

 


참고문헌-리액트를 다루는 기술(길벗)

'React' 카테고리의 다른 글

Link to, a href in React  (0) 2020.08.25
babel, webpack  (0) 2020.08.20
antd Icon  (0) 2020.08.06
Google Map  (0) 2020.08.05
antd Carousel(auto), a href  (0) 2020.08.04

관련글 더보기

댓글 영역