component구조
-
app.js
: router (Explore, Search, Feed)
: react-router-dom lib의 'Route, Switch' 사용- Explore
- Search
- Feed
- Loading
-
FeedPhoto
- PhotoActions
- photoComments
- TimeStamp
- CommentBox
- UserList : likes 누르면 나오는 팝업
- userDisplay
redux-react 설정
-
src/index.js
- reacct-redux 라이브러리 Provider로 store설정
import { Provider } from "react-redux"; ... <Provider store={store}> <App /> </Provider>
-
src/components/App/index.js
- container.js > presenter.js 호출로 Feed 컴포넌트 호출
import { connect } from 'react-redux'; import Container from './container'; const mapStateToProps = (state, ownProps) => { ... } export default connect(mapStateToProps)(Container);
-
src/components/container.js
- container에서 presenter 호출
import React from "react" import App from "./presenter" const Container = props => <App {...props} /> export default Container
-
src/components/presenter.js
<Switch> <Route> key="1" exact path="/" component={Feed}/> ... </Switch>
-
src/components/Feed/index.js
- Feed > index.js react-redux 설정
- connect: react-redux의 라이브러리에 의해서 props관련 설정/ 설정을 적용할 컴포넌트 설정
- mapDispatchToProps: redux에서 설정한 api를 세팅
- mapStateToProps: mapDispatchToProps에 의해서 변경된 state를 connect에 설정된 컴포넌트에 props로 전달
import { connect } from "react-redux" import { actionCreators as photoActions } from "redux/modules/photos" const mapStateToProps = (state, ownProps) => { const { photos: { feed }, } = state return { feed, } } const mapDispatchToProps = (dispatch, ownProps) => { return { getFeed: () => { dispatch(photoActions.getFeed()) }, } } connect(mapStateToProps, mapDispatchToProps)(Container)
retrieve - FeedPhoto 컴포넌트 rendering 과정
-
로그인 후 App/presenter.js > Privateroutes component
- privateRoutes는 Switch로 첫번째 Route는 Feed component 다.
-
Feed Component file
: index.js, container.js, presenter.js에서 작업이 이뤄짐#1. Feed/container.js > componentDidMount: this.props.getFeed 호출 #2. Feed/index.js > mapDispatchToProps function에 getFeed 호출 #3. redux/modules/photos.js > getFeed > dispatch(setFeed(json)) : dispatch reducer 호출 > reducer : applySetFeed 호출 > applySetFeed(reducer function) : return data는 mapStateToProps function 첫번째 param으로 전달 #4. Feed/index.js > mapStateToProps의 첫번째 parameter로 mapDispatchToProps의 getFeed return value를 받음 > mapStateToProps function의 return은 component의 props로 사용 #5. Feed/container.js > componentWillReceiveProps function : props를 받으면 loading을 false로 설정함으로 Loading component를 제거해준다. > render function : this.props.feed로 전달받아 Feed compoennt rendering 하는데 사용
-
componentDidMount function(in container.js)
- componentDidMount은 component가 Mount되고 lifeCycle에 의해서 호출 됨
- componentDidMount에서 this.props.getFeed를 호출
- props로 getFeed를 사용할 수 있는 이유 : “react-redux"의 connect 객체로 인해서 가능
-
mapDispatchToProps(getFeed 수행)(in index.js)
- dispatch에 설정되는 values는 photos.js(redux파일)에 "api actions" 이다.
-
redux(in photos.js) 작업
- 2번에 의해서 Photos.js에서 getFeed "api actions"호출
- getFeed success 후 'applySetFeed' reducer functions를 호츨 해 api에서 return한 value를 세팅
-
mapStateToProps function(in index.js)
- index.js > mapStateToProps에서 api에서 return한 value를 첫번째 arguments로 받을 수 있다.
- 이 과정으로 container에서 this.props로 값을 받을 수 있다.
-
componentWillReceiveProps function (in container.js)
- 윗 단계에서 return한 value를 props로 받을 수 있다.
- index.js에서 'react-redux'를 사용했기에 가능
- 여기서 component loading state를 설정 - Feed > presenter.js에서 LoadingFeed component 제어
- render function(in container.js)
- this.props에 feed가 property로 있다.
create - 댓글 달기 과정
-
enter 이후 동작 trace를 아래 순서로 설명합니다.
- event 호출
- redux
- component 전달과정
- component 구조를 잘 알고 있어야 파악하기 쉽다.
event 호출
#1. CommentBox/presenter.js
- Textarea에서 enterEvent 감지
#2. CommentBox/container.js
> _handleKeyPress 함수 동작
- this.props.submitComment함수에 작성한글(this.state.comment)넘김
#3. CommentBox/index.js
> mapDispatchToProp gkatn
- return에 적용한 submitComment 필드에 설정한 dispatch가 수행
redux
#1. modules/photos.js (redux)
> commentPhoto 함수 "api actions" 수행
- dispatch(addComment(photoId, json))에 의해 5번 호출
#2. modules/photos.js (redux)
> commentPhoto 함수 "reducer"를 수행
- fetch를 통해 서버 통신 이후
- applyAddComment 호출
#3. modules/photos.js (redux)
> applyAddComment 함수 "reducer function"를 수행
- 새로운 객체를 만들어 반환
* state를 변경하지 않는다.
* 반환된 객체는 해당 redux를 사용하는 store에 변환된 객체를 변경할 component를 찾아 dom을 update한다.
(현재는 state의 comments가 변경됐음으로 photoComments 컴포넌트가 update된다.)
component 전달과정
-
src/components/App
1. index.js : mapStateToProps function 2. container.js : <App {...props}/>; 3. presenter.js : <Route key='1' exact path='/' component={Feed} />
-
src/components/Feed
1. index.js : mapStateToProps function 2. container.js : componentWillReceiveProps function 3. container.js : render function > Feed compoennt presenter 호출 4. presenter.js : renderFeed function > FeedPhoto component index 호출
-
src/components/FeedPhoto
1. index.js : mapDispatchToProps 2. container.js : render function > FeedPhoto component presenter 호출 3. presenter.js : FeedPhoto function * Feed 화면에서 rendering 하는 component로 'PhotoComments component'를 re rendering 한다.
-
src/components/PhotoComments
1. index.js : Photo Component > Comment function * 이 Component는 보여주는 역할 밖에 없음으로 index.js에 rendering하는 부분만 있다.
create, delete - like, unlike
heart 클릭으로 빨간 heart 제거 동작 trace like하는 과정은 unlike하는 과정과 같기때문에 제거하는 것으로 설명
- enter 이후 동작 trace를 아래 순서로 설명합니다.
- event 호출
- redux
- component 전달과정
- component 구조를 잘 알고 있어야 파악하기 쉽다.
event 호출
* src/components/PhotoActions
1. presenter.js > onClick
* container에서 전달된 props에 handleHearClick 호출
2. container.js > presenter
* presneter에서 생성한 component에 props 전달(index.js에서 onClick 동작하는 function을 props에 세팅)
3. index.js > mapDispatchToprops
* handleHeartClick 호출
- dispatch(photoActions.xxx)에 호출
redux
* src/redux/modules/photos.js
1. redux > photos.js > unlikePhoto (api function)
* dispatch(doUnlikePhoto(photoId))호출
- reducer, reducer functions에 의해 state 번환으로 front에서 이미지를 제거
* fetch를 통해서 server 작업
- then에서 작업이 정상 적으로 처리가 되지 않았다면 다시 doLikePhoto를 해준다.
component 호출 과정
-
src/components/App
1. index.js : mapStateToProps function 2. container.js : <App {...props}/>; 3. presenter.js : <Route key='1' exact path='/' component={Feed} />
-
src/components/Feed
1. index.js : mapStateToProps function 2. container.js : componentWillReceiveProps function 3. container.js : render function > Feed compoennt presenter 호출 4. presenter.js : renderFeed function > FeedPhoto component index 호출
-
src/components/PhotoActions
1. index.js : mapDispatchToProps 2. container.js: render function > PhotoActions component(in presenter) 3. presenter.js: PhotoActions function * Feed 화면에서 rendering 하는 component로 하트 부분을 업데이트 해야 함으로 'PhotoActions component'를 rerendering 한다.
index.js에서 mapDispatchToProps 두번째 param(ownProps)에 대해서
- 좋아요 세팅, 해제 할때 PhotoActions > index.js에서 ownProps에 props가 다 담겨져 있다.
- 이때 'ownProps'의 값은 PhotoActions component에 property로 값을 세팅한 값이다.
- 위 PhotoActions component에 property값을 세팅한 곳은 FeedPhoto > presenter.js 이다.
delete - 댓글 삭제하기
- 서버에서 삭제하는 작업은 쉽게 마쳤으나 state관리로 화면을 rerendering하게하는 작업중 한 부분에서 걸려 생각보다 쉽지 않았다.
- 먼저 Phtocomments이 컴포넌트는 기능이 없어 index.js에 화면 rendering하는 소스만 있던 것을
index.js, container.js, presenter.js으로 나눴다. - presneter.js에 x 버튼을 달고
reducer에서 삭제api를 작성한것을 index.js에서 props로 세팅 후 x버튼 클릭식 이벤트로 등록한다. -
그리고 여기서 문제가 생겼다.
api로 component에서 원하는 값(phtoCommentId)을 전달하는 방법을 실습하는 동안에 없어 방법을 고민하다 검색으로 해결 할 수 있었다.render: function () { var children = this.state.childrenData.map(function(childData,childIndex) { return <Child onClick={this.handleChildClick.bind(null,childData)} text={childData.childText}/>; }.bind(this)); return <div>{children}</div>; },
- 참고주소
- 아래와 같이 컴포넌트 관계가 있다.
- Explore > Feed > FeedPhoto > photoComments 여기서 photoComments에서 삭제하는 photoId, photoCommentId를 전달해야만 했다.
- 그래서 photoComments에 photoId를 갖기 위해서 FeedPhotod component에 photoComments컴포넌트에 photoId를 세팅해준다.
- photoCommentId는 이미 PhotoComment component에서 세팅해줬다.
해줘야 할 것은 api로 전달해야 하는 값을 presneter.js에서 index.js에 reducer api로 전달할 수 있도록 index.js에 전달해줘야 했다.