4. 컨테이너 컴포넌트 만들기

그럼 이제 리덕스와 연동되어있는 컨테이너 컴포넌트를 만들어봅시다! 우리는 ViewerContainer 와 SpaceNavigatorContainer 를 만들게 됩니다.

ViewerContainer

먼저 ViewerContainer 컴포넌트를 만들어봅시다. 이 컴포넌트에서는 componentDidMount 가 발생하면 getApod API 를 호출하게 됩니다. 추가적으로, componentDidUpdate 에서 props 로 받은 date 가 바뀌게 될 때에도 API 를 호출합니다.

connect 하는 과정에서, 로딩 상태는 pender 리듀서의 pending 객체 내부의 액션 이름을 키로 가지는 값 pender.pending['apod/GET_APOD'] 를 조회하면 됩니다.

그리고, redux-pender 를 통하여 전달된 프로미스는 .cancel() 을 호출하여 기존에 넣었던 프로미스가 무시되게끔 처리를 할 수 있습니다. 따라서, 요청을 하게 되면 멤버 변수 req 에 해당 프로미스를 담아두고, getApod 메소드가 다시 호출 될 때 이미 대기중인 요청이 있다면 cancel 을 호출해서 기존의 요청이 무시되도록 설정하고 새 요청을 넣습니다.

`src/containers/ViewerContainer.js

import React, { Component } from 'react';
import Viewer from 'components/Viewer';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as apodActions from 'store/modules/apod';

class ViewerContainer extends Component {
  req = null

  getApod = async () => {
    const { ApodActions, loading, date } = this.props;
    loading && this.req.cancel(); // 로딩중이라면 취소하기

    try {
      // this.req 에 Promise 담기
      this.req = ApodActions.getApod(date || '');
      await this.req; // 끝날 때 까지 대기
      // console.log(this.props.url);
    } catch (e) {
      console.log(e);
    }
  }

  componentDidMount() {
    // 컴포넌트가 처음 나타날 때 요청
    this.getApod();
  }

  componentDidUpdate(prevProps, prevState) {
    // date 가 변경되면 요청
    if(this.props.date !== prevProps.date) {
      this.getApod();
    }
  }


  render() {
    const { date, url ,mediaType, loading } = this.props;
    return (
      <Viewer 
        date={date}
        url={url}
        mediaType={mediaType}
        loading={loading}
      />
    );
  }
}

export default connect(
  ({ apod, pender }) => ({
    date: apod.get('date'),
    url: apod.get('url'),
    mediaType: apod.get('mediaType'),
    loading: pender.pending['apod/GET_APOD']
  }),
  (dispatch) => ({
    ApodActions: bindActionCreators(apodActions, dispatch)
  })
)(ViewerContainer);

다 작성하셨으면, App 컴포넌트에서 기존의 Viewer 대신 렌더링하세요.

src/App.js

import React, { Component } from 'react';
import ViewerTemplate from './ViewerTemplate';
import SpaceNavigator from 'components/SpaceNavigator';
import ViewerContainer from 'containers/ViewerContainer';

class App extends Component {
  render() {
    return (
      <ViewerTemplate
        spaceNavigator={<SpaceNavigator/>}
        viewer={(
          <ViewerContainer/>
        )}
      />
    );
  }
}

export default App;

화면에 오늘의 우주 사진이 잘 나타났나요?

SpaceNavigatorContainer

자, 이제 화살표 버튼들도 제대로 작동하게끔 해줍시다! 이 부분은 꽤 간단합니다! handlePrev 함수와 handleNext 함수를 구현해서 ApodActions 안에 있는 액션 생성 함수들을 호출하여 SpaceNavigator 에게 전달해주시면 됩니다.

그 과정에서, connect 를 사용하여 date 와 maxDate 를 받아와서, 만약에 이 두 값이 동일할 경우엔 그 다음날짜로 이동하지 못하게끔 구현하세요.

src/containers/SpaceNavigatorContainer.js

import React, { Component } from 'react';
import SpaceNavigator from 'components/SpaceNavigator';
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux';
import * as apodActions from 'store/modules/apod';

class SpaceNavigatorContainer extends Component {
  handlePrev = () => {
    const { ApodActions } = this.props;
    ApodActions.previous();
  }
  handleNext = () => {
    const { ApodActions, date, maxDate } = this.props;
    if(date === maxDate) return; // 오늘이면 여기서 스탑
    ApodActions.next();
  }
  render() {
    const { handlePrev, handleNext } = this;
    return (
      <SpaceNavigator 
        onPrev={handlePrev}
        onNext={handleNext}
      />
    );
  }
}

export default connect(
  ({apod}) => ({
    date: apod.get('date'),
    maxDate: apod.get('maxDate')
  }),
  (dispatch) => ({
    ApodActions: bindActionCreators(apodActions, dispatch)
  })
)(SpaceNavigatorContainer);

모든게 잘 작동하나요?

results matching ""

    No results matching ""