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 에서 열람 할 수 있습니다.