5-8. ContactListContainer 컴포넌트 만들기

자, 우리의 마지막 컨테이너 ContactListContainer 를 만들어보겠습니다.

src/containers/ContactListContainer.js

이번 컴포넌트에서, handleOpenModify 함수를 만들 때, 주어진 id 를 가지고 주소록 데이터를 가져올 때에는, Map의 내장함수 find 를 사용하세요. 이 함수는 조건이 일치하는 데이터를 반환합니다.

import React, { Component } from 'react';
import ContactList from '../components/ContactList';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import * as modalActions from '../modules/modal';
import * as contactsActions from '../modules/contacts';

class ContactListContainer extends Component {
    // 수정 모달 열기
    handleOpenModify = (id) => {
        const { contacts, ModalActions } = this.props;

        // id 로 contact 조회
        const contact = contacts.find(contact => contact.get('id') === id);

        ModalActions.show({
            mode: 'modify',
            contact: contact.toJS()
        });
    }


    // 즐겨찾기 활성화 / 비활성화
    handleToggleFavorite = (id) => {
        const { ContactsActions } = this.props;
        ContactsActions.toggleFavorite(id);
    }


    render() {
        const { contacts, keyword } = this.props;
        const { 
            handleOpenModify,
            handleToggleFavorite
        } = this;

        return (
            <ContactList
                contacts={contacts}
                onOpenModify={handleOpenModify}
                onToggleFavorite={handleToggleFavorite}
                search={keyword}
            />
        );
    }
}

export default connect(
    (state) => ({
        keyword: state.base.get('keyword'),
        contacts: state.contacts
    }),
    (dispatch) => ({
        ModalActions: bindActionCreators(modalActions, dispatch),
        ContactsActions: bindActionCreators(contactsActions, dispatch)
    })
)(ContactListContainer);

ContactList / ContactItem 컴포넌트 수정하기

아직 끝이 아닙니다! 우리가 아까 FavoriteListContainer 를 만들었을때 다른 컴포넌트를 수정해야됐던것처럼, 이 컴포넌트도 마찬가지로, ContactList 컴포넌트와 ContactItem 컴포넌트가 Map 그리고 List 인스턴스를 다룰 수 있도록 수정을 해주세요.

src/components/ContactList.js

import React, { Component } from 'react';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import ContactItem from './ContactItem';
import CSSTransitionGroup from 'react-transition-group/CSSTransitionGroup';
import { transitions } from '../lib/style-utils';
import ImmutablePropTypes from 'react-immutable-proptypes';

const Wrapper = styled.div`
    margin-top: 1rem;

    .contact-enter {
        animation: ${transitions.stretchOut} .15s linear;
        animation-fill-mode: forwards;
    }

    .contact-leave {
        animation: ${transitions.shrinkIn} .15s linear;
        animation-fill-mode: forwards;
    }

`;

class ContactList extends Component {

    static propTypes = {
        contacts: ImmutablePropTypes.listOf(
            ImmutablePropTypes.mapContains({
                id: PropTypes.string,
                name: PropTypes.string,
                phone: PropTypes.string,
                color: PropTypes.string,
                favorite: PropTypes.bool
            })
        ),
        search: PropTypes.string, // 검색 키워드
        onToggleFavorite: PropTypes.func, // 즐겨찾기 토글
        onOpenModify: PropTypes.func // 수정 모달 띄우기
    }

    render() {
        const { contacts, onOpenModify, search, onToggleFavorite } = this.props;

        const contactList = contacts
                            .filter( // 키워드로 필터링
                                c => c.get('name').indexOf(search) !== -1
                            ).sort( // 가나다순으로 정렬
                                (a,b) => {
                                    if(a.get('name') > b.get('name')) return 1;
                                    if (a.get('name') < b.get('name')) return -1;
                                    return 0;
                                }
                            ).map( // 컴포넌트로 매핑
                                contact => (
                                    <ContactItem 
                                        key={contact.get('id')} 
                                        contact={contact}
                                        onOpenModify={onOpenModify}
                                        onToggleFavorite={onToggleFavorite}
                                    />
                                )
                            );


        return (
            <Wrapper>
                <CSSTransitionGroup
                        transitionName="contact"
                        transitionEnterTimeout={500}
                        transitionLeaveTimeout={500}>
                {contactList}
                </CSSTransitionGroup>
            </Wrapper>
        );
    }
}

export default ContactList;

src/components/ContactItem.js

import React, { Component } from 'react';
import styled from 'styled-components';
import oc from 'open-color';
import PropTypes from 'prop-types';
import Thumbnail from './Thumbnail';
import StarIcon from 'react-icons/lib/md/star';
import EditIcon from 'react-icons/lib/md/edit';

import ImmutablePropTypes from 'react-immutable-proptypes';


/* styled 컴포넌트 생략 */


class ContactItem extends Component {

    static propTypes = {
        contact: ImmutablePropTypes.mapContains({
            id: PropTypes.string,
            name: PropTypes.string,
            phone: PropTypes.string,
            color: PropTypes.string,
            favorite: PropTypes.bool
        }),
        onToggleFavorite: PropTypes.func,
        onOpenModify: PropTypes.func
    }

    render() {
        // 레퍼런스 준비
        const {
            contact,
            onOpenModify,
            onToggleFavorite
        } = this.props;

        const { name, phone, favorite, id, color } = contact.toJS();

        return (
            <Wrapper>
                <Thumbnail color={color}/>
                <Info>
                    <Name>{name}</Name>
                    <Phone>{phone}</Phone>
                </Info>
                <div className="actions">
                    <CircleButton className="favorite" active={favorite} onClick={() => onToggleFavorite(id)}>
                        <StarIcon/>
                    </CircleButton>
                    <CircleButton onClick={() => onOpenModify(id)}>
                        <EditIcon/>
                    </CircleButton>
                </div>
            </Wrapper>
        );
    }
}

export default ContactItem;

App 에서 불러와서 사용하기

자! 우리의 마지막 컨테이너 컴포넌트를 불러와서 사용해봅시다.

import React, { Component } from 'react';
import Header from './components/Header';
import Container from './components/Container';
import { connect } from 'react-redux'

import ViewSelectorContainer from './containers/ViewSelectorContainer';
import InputContainer from './containers/InputContainer';
import FavoriteListContainer from './containers/FavoriteListContainer';
import FloatingButtonContainer from './containers/FloatingButtonContainer';
import ContactModalContainer from './containers/ContactModalContainer';
import ContactListContainer from './containers/ContactListContainer';


class App extends Component {
    render() {
        // 레퍼런스 준비
        const { view } = this.props;

        return (
            <div>
                <Header/>
                <ViewSelectorContainer/>

                {/* view 값에 따라 다른 컨테이너를 보여준다 */}
                <Container visible={view==='favorite'}>
                    <FavoriteListContainer/>
                </Container>
                <Container visible={view==='list'}>
                    <InputContainer/>
                    <ContactListContainer/>
                </Container>

                <ContactModalContainer/>
                <FloatingButtonContainer/>
            </div>
        );
    }
}

export default connect(
    (state) => ({
        view: state.base.get('view')
    })
)(App);

자! 작업을 모두 마쳤습니다!

모든 기능이 작동하는지 확인하세요.

지금까지의 코드는 Github 에서 열람 할 수 있습니다.

results matching ""

    No results matching ""