인프런 커뮤니티 질문&답변
user/[id].js => Unhandled Runtime Error TypeError: Cannot read property 'id' of undefined 에러가 발생합니다.
작성
·
314
0
페이지 로딩은 정상적으로 되는데 스크롤시 에러가 발생합니다.
<Head> 태그 url 부분을 약간 다르게 수정해 주었고 다른 부분은 이상이 없어 보입니다.
로그를 찍어 봤는데.. 값이 나오는데 값을 찾을수 없다...????
user/[id].js
const User = () => {
const dispatch = useDispatch();
const router = useRouter();
const { id } = router.query; <==== 이부분에서 id 값은 잘 받아 오는것 같습니다.
console.log(router.query); <==== 로그를 찍어보니 {id: "1"} 로 나옵니다. github에 올리신 파일로 돌려서 로그를 찍어 본 결과 값도 동일하게 string 인데.. 정상적으로 스크롤 됩니다.
const { mainPosts, hasMorePosts, loadPostsLoading } = useSelector((state) => state.post);
const { userInfo } = useSelector((state) => state.user);

FRONT
user/[id]/.js
// user/[id].js
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { Avatar, Card } from 'antd';
import { END } from 'redux-saga';
import Head from 'next/head';
import { useRouter } from 'next/router';
import axios from 'axios';
import { LOAD_USER_POSTS_REQUEST } from '../../reducers/post';
import { LOAD_MY_INFO_REQUEST, LOAD_USER_REQUEST } from '../../reducers/user';
import PostCard from '../../components/PostCard';
import wrapper from '../../store/configureStore';
import AppLayout from '../../components/AppLayout';
import { backUrl } from '../../config/config';
// 프론트서버와 브라우저 모두에서 실행
const User = () => {
const dispatch = useDispatch();
const router = useRouter();
const { id } = router.query; <==== 이부분에서 id 값은 잘 받아 오는것 같습니다.
console.log(router.query); <==== 로그를 찍어보니 {id: "1"} 로 나옵니다. github에 올리신 파일로 돌려서 로그를 찍어 본 결과도 동일한데.. 정상적으로 스크롤 됩니다.
const { mainPosts, hasMorePosts, loadPostsLoading } = useSelector((state) => state.post);
const { userInfo } = useSelector((state) => state.user);
useEffect(() => {
function onScroll() {
if (window.scrollY + document.documentElement.clientHeight > document.documentElement.scrollHeight - 300) {
if (hasMorePosts && !loadPostsLoading) {
dispatch({
type: LOAD_USER_POSTS_REQUEST,
lastId: mainPosts[mainPosts.length - 1] && mainPosts[mainPosts.lenth - 1].id, <====== 에러가 발생하는 부분
data: id,
});
}
}
}
window.addEventListener('scroll', onScroll);
return () => {
window.removeEventListener('scroll', onScroll);
};
}, [ mainPosts.length, hasMorePosts, id]);
return (
<AppLayout>
<Head>
<title>
{userInfo.nickname}
님의 글
</title>
<meta name="description" content={`${userInfo.nickname}님의 게시글`} />
<meta property="og:title" content={`${userInfo.nickname}님의 게시글`} />
<meta property="og:description" content={`${userInfo.nickname}님의 게시글`} />
<meta property="og:image" content={`${backUrl}/favicon.ico`} />
<meta property="og:url" content={`${backUrl}/user/${id}`} />
</Head>
{userInfo
? (
<Card
actions={[
<div key="twit">
짹짹
<br />
{userInfo.Posts}
</div>,
<div key="following">
팔로잉
<br />
{userInfo.Followings}
</div>,
<div key="follower">
팔로워
<br />
{userInfo.Followers}
</div>,
]}
>
<Card.Meta
avatar={<Avatar>{userInfo.nickname[0]}</Avatar>}
title={userInfo.nickname}
/>
</Card>
)
: null}
{mainPosts.map((c) => (
<PostCard key={c.id} post={c} />
))}
</AppLayout>
);
};
// front server 에서 실행
export const getServerSideProps = wrapper.getServerSideProps(async (context) => {
const cookie = context.req ? context.req.headers.cookie : '';
axios.defaults.headers.Cookie = '';
if (context.req && cookie) {
axios.defaults.headers.Cookie = cookie;
}
context.store.dispatch({
type: LOAD_USER_POSTS_REQUEST,
data: context.params.id,
});
context.store.dispatch({
type: LOAD_MY_INFO_REQUEST,
});
context.store.dispatch({
type: LOAD_USER_REQUEST,
data: context.params.id,
});
context.store.dispatch(END);
await context.store.sagaTask.toPromise();
// return { props: {} };
});
export default User;
config/config.js
const domainUrl = 'https://api.nodebird.com';
const localhostUrl = 'http://localhost:3065';
const backUrl = process.env.NODE_ENV === 'production' ? 'https://api.nodebird.com' : 'http://localhost:3065';
export { backUrl, domainUrl, localhostUrl };
BACK
sagas/user.js
// all fork call put delay debounce throttle takeLatest tabkeEvery takeLeding taekMaybe
import { all, fork, put, takeLatest, throttle, call } from 'redux-saga/effects';
import axios from 'axios';
// import shortId from 'shortid';
import {
LOAD_POSTS_REQUEST,
LOAD_POSTS_SUCCESS,
LOAD_POSTS_FAILURE,
LOAD_USER_POSTS_REQUEST,
LOAD_USER_POSTS_SUCCESS,
LOAD_USER_POSTS_FAILURE,
LOAD_HASHTAG_POSTS_REQUEST,
LOAD_HASHTAG_POSTS_SUCCESS,
LOAD_HASHTAG_POSTS_FAILURE,
LOAD_POST_REQUEST,
LOAD_POST_SUCCESS,
LOAD_POST_FAILURE,
LIKE_POST_REQUEST,
LIKE_POST_SUCCESS,
LIKE_POST_FAILURE,
UNLIKE_POST_REQUEST,
UNLIKE_POST_SUCCESS,
UNLIKE_POST_FAILURE,
ADD_POST_REQUEST,
ADD_POST_SUCCESS,
ADD_POST_FAILURE,
ADD_COMMENT_REQUEST,
ADD_COMMENT_SUCCESS,
ADD_COMMENT_FAILURE,
REMOVE_POST_REQUEST,
REMOVE_POST_SUCCESS,
REMOVE_POST_FAILURE,
UPLOAD_IMAGES_REQUEST,
UPLOAD_IMAGES_SUCCESS,
UPLOAD_IMAGES_FAILURE,
RETWEET_REQUEST,
RETWEET_SUCCESS,
RETWEET_FAILURE,
} from '../reducers/post';
import {
ADD_POST_TO_ME,
REMOVE_POST_OF_ME,
} from '../reducers/user';
function likePostAPI(data) {
return axios.patch(`/post/${data}/like`);
};
function* likePost(action) {
try {
const result = yield call(likePostAPI, action.data);
yield put({
type: LIKE_POST_SUCCESS,
data: result.data,
});
} catch (err) {
console.error(err);
yield put({ // put => dispatch 다.
type: LIKE_POST_FAILURE,
error: err.response.data,
});
}
}
function unlikePostAPI(data) {
return axios.delete(`/post/${data}/like`);
};
function* unlikePost(action) {
try {
const result = yield call(unlikePostAPI, action.data);
yield put({
type: UNLIKE_POST_SUCCESS,
data: result.data,
});
} catch (err) {
console.error(err);
yield put({ // put => dispatch 다.
type: UNLIKE_POST_FAILURE,
error: err.response.data,
});
}
}
function loadPostsAPI(lastId) {
return axios.get(`/posts?lastId=${lastId || 0}`);
};
function* loadPosts(action) {
try {
const result = yield call(loadPostsAPI, action.lastId);
yield put({
type: LOAD_POSTS_SUCCESS,
data: result.data,
// data: generateDummyPost(10),
});
} catch (err) {
console.error(err);
yield put({ // put => dispatch 다.
type: LOAD_POSTS_FAILURE,
error: err.response.data,
});
}
}
function loadPostAPI(data) {
return axios.get(`/post/${data}`);
};
function* loadPost(action) {
try {
const result = yield call(loadPostAPI, action.data);
yield put({
type: LOAD_POST_SUCCESS,
data: result.data,
// data: generateDummyPost(10),
});
} catch (err) {
console.error(err);
yield put({ // put => dispatch 다.
type: LOAD_POST_FAILURE,
error: err.response.data,
});
}
}
function addCommentAPI(data) {
return axios.post(`/post/${data.postId}/comment`, data); // POST /post/1/comment
};
function* addComment(action) {
try {
const result = yield call(addCommentAPI, action.data);
// yield delay(1000);
yield put({
type: ADD_COMMENT_SUCCESS,
data: result.data,
});
} catch (err) {
console.error(err);
yield put({ // put => dispatch 다.
type: ADD_COMMENT_FAILURE,
error: err.response.data,
});
};
};
function addPostAPI(data) {
return axios.post('/post', data);
};
function* addPost(action) {
try {
const result = yield call(addPostAPI, action.data);
yield put({
type: ADD_POST_SUCCESS,
data: result.data
});
yield put({
type: ADD_POST_TO_ME,
data: result.data.id,
});
} catch (err) {
console.error(err);
yield put({ // put => dispatch 다.
type: ADD_POST_FAILURE,
error: err.response.data,
});
};
};
function removePostAPI(data) {
return axios.delete(`/post/${data}`);
};
function* removePost(action) {
try {
const result = yield call(removePostAPI, action.data);
yield put({
type: REMOVE_POST_SUCCESS,
data: result.data,
});
console.log('removePost');
yield put({
type: REMOVE_POST_OF_ME,
data: action.data,
});
} catch (err) {
console.error(err);
yield put({ // put => dispatch 다.
type: REMOVE_POST_FAILURE,
error: err.response.data,
});
};
};
function uploadImagesAPI(data) {
return axios.post('/post/images', data); // POST /post/images
};
function* uploadImages(action) {
try {
const result = yield call(uploadImagesAPI, action.data);
yield put({
type: UPLOAD_IMAGES_SUCCESS,
data: result.data,
});
} catch (err) {
console.error(err);
yield put({ // put => dispatch 다.
type: UPLOAD_IMAGES_FAILURE,
error: err.response.data,
});
};
};
function retweetAPI(data) {
return axios.post(`/post/${data}/retweet`); // POST /post/images
};
function* retweet(action) {
try {
const result = yield call(retweetAPI, action.data);
yield put({
type: RETWEET_SUCCESS,
data: result.data,
});
} catch (err) {
console.error(err);
yield put({ // put => dispatch 다.
type: RETWEET_FAILURE,
error: err.response.data,
});
};
};
function loadHashtagPostsAPI(data, lastId) {
return axios.get(`/hashtag/${data}?lastId=${lastId || 0}`);
};
function* loadHashtagPosts(action) {
try {
const result = yield call(loadHashtagPostsAPI, action.data, action.lastId);
yield put({
type: LOAD_HASHTAG_POSTS_SUCCESS,
data: result.data,
});
} catch (err) {
console.error(err);
yield put({
type: LOAD_HASHTAG_POSTS_FAILURE,
error: err.response.data,
});
}
}
function loadUserPostsAPI(data, lastId) {
return axios.get(`/user/${data}/posts?lastId=${lastId || 0}`);
};
function* loadUserPosts(action) {
try {
const result = yield call(loadUserPostsAPI, action.data, action.lastId);
yield put({
type: LOAD_USER_POSTS_SUCCESS,
data: result.data,
// data: generateDummyPost(10),
});
} catch (err) {
console.error(err);
yield put({ // put => dispatch 다.
type: LOAD_USER_POSTS_FAILURE,
error: err.response.data,
});
}
}
function* watchLoadUserPosts() {
yield throttle(5000, LOAD_USER_POSTS_REQUEST, loadUserPosts);
};
function* watchLoadHashtagPosts() {
// 5초에 한번 게사글이 로드 된다.
yield throttle(5000, LOAD_HASHTAG_POSTS_REQUEST, loadHashtagPosts);
};
function* watchLoadPosts() {
// 5초에 한번 게사글이 로드 된다.
yield throttle(5000, LOAD_POSTS_REQUEST, loadPosts);
};
function* watchLoadPost() {
// 5초에 한번 게사글이 로드 된다.
yield takeLatest(LOAD_POST_REQUEST, loadPost);
};
function* watchRetweet() {
// 5초에 한번 게사글이 로드 된다.
yield takeLatest(RETWEET_REQUEST, retweet);
};
function* watchAddPost() {
yield takeLatest(ADD_POST_REQUEST, addPost);
};
function* watchUploadImages() {
yield takeLatest(UPLOAD_IMAGES_REQUEST, uploadImages);
};
function* watchAddComment() {
yield takeLatest(ADD_COMMENT_REQUEST, addComment);
};
function* watchRemovePost() {
yield takeLatest(REMOVE_POST_REQUEST, removePost);
};
function* watchLikePost() {
yield takeLatest(LIKE_POST_REQUEST, likePost);
};
function* watchUnLikePost() {
yield takeLatest(UNLIKE_POST_REQUEST, unlikePost);
};
export default function* postSaga() {
yield all([
fork(watchRetweet),
fork(watchUploadImages),
fork(watchLikePost),
fork(watchUnLikePost),
fork(watchAddPost),
fork(watchLoadPosts),
fork(watchLoadHashtagPosts),
fork(watchLoadUserPosts),
fork(watchLoadPost),
fork(watchRemovePost),
fork(watchAddComment),
]);
};
reducers/post.js
reducers/user.js
FRONT
user/[id]/.js
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { Avatar, Card } from 'antd';
import { END } from 'redux-saga';
import Head from 'next/head';
import { useRouter } from 'next/router';
import axios from 'axios';
import { LOAD_USER_POSTS_REQUEST } from '../../reducers/post';
import { LOAD_MY_INFO_REQUEST, LOAD_USER_REQUEST } from '../../reducers/user';
import PostCard from '../../components/PostCard';
import wrapper from '../../store/configureStore';
import AppLayout from '../../components/AppLayout';
import { backUrl } from '../../config/config';
// 프론트서버와 브라우저 모두에서 실행
const User = () => {
const dispatch = useDispatch();
const router = useRouter();
const { id } = router.query; <==== 이부분에서 id 값은 잘 받아 오는것 같습니다.
console.log(router.query); <==== 로그를 찍어보니 {id: "1"} 로 나옵니다. github에 올리신 파일로 돌려서 로그를 찍어 본 결과도 동일한데.. 정상적으로 스크롤 됩니다.
const { mainPosts, hasMorePosts, loadPostsLoading } = useSelector((state) => state.post);
const { userInfo } = useSelector((state) => state.user);
useEffect(() => {
function onScroll() {
if (window.scrollY + document.documentElement.clientHeight > document.documentElement.scrollHeight - 300) {
if (hasMorePosts && !loadPostsLoading) {
dispatch({
type: LOAD_USER_POSTS_REQUEST,
lastId: mainPosts[mainPosts.length - 1] && mainPosts[mainPosts.lenth - 1].id, <====== 에러가 발생하는 부분
data: id,
});
}
}
}
window.addEventListener('scroll', onScroll);
return () => {
window.removeEventListener('scroll', onScroll);
};
}, [ mainPosts.length, hasMorePosts, id]);
return (
<AppLayout>
<Head>
<title>
{userInfo.nickname}
님의 글
</title>
<meta name="description" content={`${userInfo.nickname}님의 게시글`} />
<meta property="og:title" content={`${userInfo.nickname}님의 게시글`} />
<meta property="og:description" content={`${userInfo.nickname}님의 게시글`} />
<meta property="og:image" content={`${backUrl}/favicon.ico`} />
<meta property="og:url" content={`${backUrl}/user/${id}`} />
</Head>
{userInfo
? (
<Card
actions={[
<div key="twit">
짹짹
<br />
{userInfo.Posts}
</div>,
<div key="following">
팔로잉
<br />
{userInfo.Followings}
</div>,
<div key="follower">
팔로워
<br />
{userInfo.Followers}
</div>,
]}
>
<Card.Meta
avatar={<Avatar>{userInfo.nickname[0]}</Avatar>}
title={userInfo.nickname}
/>
</Card>
)
: null}
{mainPosts.map((c) => (
<PostCard key={c.id} post={c} />
))}
</AppLayout>
);
};
// front server 에서 실행
export const getServerSideProps = wrapper.getServerSideProps(async (context) => {
const cookie = context.req ? context.req.headers.cookie : '';
axios.defaults.headers.Cookie = '';
if (context.req && cookie) {
axios.defaults.headers.Cookie = cookie;
}
context.store.dispatch({
type: LOAD_USER_POSTS_REQUEST,
data: context.params.id,
});
context.store.dispatch({
type: LOAD_MY_INFO_REQUEST,
});
context.store.dispatch({
type: LOAD_USER_REQUEST,
data: context.params.id,
});
context.store.dispatch(END);
await context.store.sagaTask.toPromise();
// return { props: {} };
});
export default User;
config/config.js
const domainUrl = 'https://api.nodebird.com';
const localhostUrl = 'http://localhost:3065';
const backUrl = process.env.NODE_ENV === 'production' ? 'https://api.nodebird.com' : 'http://localhost:3065';
export { backUrl, domainUrl, localhostUrl };
BACK
sagas/user.js
// all fork call put delay debounce throttle takeLatest tabkeEvery takeLeding taekMaybe
import { all, fork, put, takeLatest, throttle, call } from 'redux-saga/effects';
import axios from 'axios';
// import shortId from 'shortid';
import {
LOAD_POSTS_REQUEST,
LOAD_POSTS_SUCCESS,
LOAD_POSTS_FAILURE,
LOAD_USER_POSTS_REQUEST,
LOAD_USER_POSTS_SUCCESS,
LOAD_USER_POSTS_FAILURE,
LOAD_HASHTAG_POSTS_REQUEST,
LOAD_HASHTAG_POSTS_SUCCESS,
LOAD_HASHTAG_POSTS_FAILURE,
LOAD_POST_REQUEST,
LOAD_POST_SUCCESS,
LOAD_POST_FAILURE,
LIKE_POST_REQUEST,
LIKE_POST_SUCCESS,
LIKE_POST_FAILURE,
UNLIKE_POST_REQUEST,
UNLIKE_POST_SUCCESS,
UNLIKE_POST_FAILURE,
ADD_POST_REQUEST,
ADD_POST_SUCCESS,
ADD_POST_FAILURE,
ADD_COMMENT_REQUEST,
ADD_COMMENT_SUCCESS,
ADD_COMMENT_FAILURE,
REMOVE_POST_REQUEST,
REMOVE_POST_SUCCESS,
REMOVE_POST_FAILURE,
UPLOAD_IMAGES_REQUEST,
UPLOAD_IMAGES_SUCCESS,
UPLOAD_IMAGES_FAILURE,
RETWEET_REQUEST,
RETWEET_SUCCESS,
RETWEET_FAILURE,
} from '../reducers/post';
import {
ADD_POST_TO_ME,
REMOVE_POST_OF_ME,
} from '../reducers/user';
function likePostAPI(data) {
return axios.patch(`/post/${data}/like`);
};
function* likePost(action) {
try {
const result = yield call(likePostAPI, action.data);
yield put({
type: LIKE_POST_SUCCESS,
data: result.data,
});
} catch (err) {
console.error(err);
yield put({ // put => dispatch 다.
type: LIKE_POST_FAILURE,
error: err.response.data,
});
}
}
function unlikePostAPI(data) {
return axios.delete(`/post/${data}/like`);
};
function* unlikePost(action) {
try {
const result = yield call(unlikePostAPI, action.data);
yield put({
type: UNLIKE_POST_SUCCESS,
data: result.data,
});
} catch (err) {
console.error(err);
yield put({ // put => dispatch 다.
type: UNLIKE_POST_FAILURE,
error: err.response.data,
});
}
}
function loadPostsAPI(lastId) {
return axios.get(`/posts?lastId=${lastId || 0}`);
};
function* loadPosts(action) {
try {
const result = yield call(loadPostsAPI, action.lastId);
yield put({
type: LOAD_POSTS_SUCCESS,
data: result.data,
// data: generateDummyPost(10),
});
} catch (err) {
console.error(err);
yield put({ // put => dispatch 다.
type: LOAD_POSTS_FAILURE,
error: err.response.data,
});
}
}
function loadPostAPI(data) {
return axios.get(`/post/${data}`);
};
function* loadPost(action) {
try {
const result = yield call(loadPostAPI, action.data);
yield put({
type: LOAD_POST_SUCCESS,
data: result.data,
// data: generateDummyPost(10),
});
} catch (err) {
console.error(err);
yield put({ // put => dispatch 다.
type: LOAD_POST_FAILURE,
error: err.response.data,
});
}
}
function addCommentAPI(data) {
return axios.post(`/post/${data.postId}/comment`, data); // POST /post/1/comment
};
function* addComment(action) {
try {
const result = yield call(addCommentAPI, action.data);
// yield delay(1000);
yield put({
type: ADD_COMMENT_SUCCESS,
data: result.data,
});
} catch (err) {
console.error(err);
yield put({ // put => dispatch 다.
type: ADD_COMMENT_FAILURE,
error: err.response.data,
});
};
};
function addPostAPI(data) {
return axios.post('/post', data);
};
function* addPost(action) {
try {
const result = yield call(addPostAPI, action.data);
yield put({
type: ADD_POST_SUCCESS,
data: result.data
});
yield put({
type: ADD_POST_TO_ME,
data: result.data.id,
});
} catch (err) {
console.error(err);
yield put({ // put => dispatch 다.
type: ADD_POST_FAILURE,
error: err.response.data,
});
};
};
function removePostAPI(data) {
return axios.delete(`/post/${data}`);
};
function* removePost(action) {
try {
const result = yield call(removePostAPI, action.data);
yield put({
type: REMOVE_POST_SUCCESS,
data: result.data,
});
console.log('removePost');
yield put({
type: REMOVE_POST_OF_ME,
data: action.data,
});
} catch (err) {
console.error(err);
yield put({ // put => dispatch 다.
type: REMOVE_POST_FAILURE,
error: err.response.data,
});
};
};
function uploadImagesAPI(data) {
return axios.post('/post/images', data); // POST /post/images
};
function* uploadImages(action) {
try {
const result = yield call(uploadImagesAPI, action.data);
yield put({
type: UPLOAD_IMAGES_SUCCESS,
data: result.data,
});
} catch (err) {
console.error(err);
yield put({ // put => dispatch 다.
type: UPLOAD_IMAGES_FAILURE,
error: err.response.data,
});
};
};
function retweetAPI(data) {
return axios.post(`/post/${data}/retweet`); // POST /post/images
};
function* retweet(action) {
try {
const result = yield call(retweetAPI, action.data);
yield put({
type: RETWEET_SUCCESS,
data: result.data,
});
} catch (err) {
console.error(err);
yield put({ // put => dispatch 다.
type: RETWEET_FAILURE,
error: err.response.data,
});
};
};
function loadHashtagPostsAPI(data, lastId) {
return axios.get(`/hashtag/${data}?lastId=${lastId || 0}`);
};
function* loadHashtagPosts(action) {
try {
const result = yield call(loadHashtagPostsAPI, action.data, action.lastId);
yield put({
type: LOAD_HASHTAG_POSTS_SUCCESS,
data: result.data,
});
} catch (err) {
console.error(err);
yield put({
type: LOAD_HASHTAG_POSTS_FAILURE,
error: err.response.data,
});
}
}
function loadUserPostsAPI(data, lastId) {
return axios.get(`/user/${data}/posts?lastId=${lastId || 0}`);
};
function* loadUserPosts(action) {
try {
const result = yield call(loadUserPostsAPI, action.data, action.lastId);
yield put({
type: LOAD_USER_POSTS_SUCCESS,
data: result.data,
// data: generateDummyPost(10),
});
} catch (err) {
console.error(err);
yield put({ // put => dispatch 다.
type: LOAD_USER_POSTS_FAILURE,
error: err.response.data,
});
}
}
function* watchLoadUserPosts() {
yield throttle(5000, LOAD_USER_POSTS_REQUEST, loadUserPosts);
};
function* watchLoadHashtagPosts() {
// 5초에 한번 게사글이 로드 된다.
yield throttle(5000, LOAD_HASHTAG_POSTS_REQUEST, loadHashtagPosts);
};
function* watchLoadPosts() {
// 5초에 한번 게사글이 로드 된다.
yield throttle(5000, LOAD_POSTS_REQUEST, loadPosts);
};
function* watchLoadPost() {
// 5초에 한번 게사글이 로드 된다.
yield takeLatest(LOAD_POST_REQUEST, loadPost);
};
function* watchRetweet() {
// 5초에 한번 게사글이 로드 된다.
yield takeLatest(RETWEET_REQUEST, retweet);
};
function* watchAddPost() {
yield takeLatest(ADD_POST_REQUEST, addPost);
};
function* watchUploadImages() {
yield takeLatest(UPLOAD_IMAGES_REQUEST, uploadImages);
};
function* watchAddComment() {
yield takeLatest(ADD_COMMENT_REQUEST, addComment);
};
function* watchRemovePost() {
yield takeLatest(REMOVE_POST_REQUEST, removePost);
};
function* watchLikePost() {
yield takeLatest(LIKE_POST_REQUEST, likePost);
};
function* watchUnLikePost() {
yield takeLatest(UNLIKE_POST_REQUEST, unlikePost);
};
export default function* postSaga() {
yield all([
fork(watchRetweet),
fork(watchUploadImages),
fork(watchLikePost),
fork(watchUnLikePost),
fork(watchAddPost),
fork(watchLoadPosts),
fork(watchLoadHashtagPosts),
fork(watchLoadUserPosts),
fork(watchLoadPost),
fork(watchRemovePost),
fork(watchAddComment),
]);
};
reducers/post.js
import produce from 'immer';
import {
REMOVE_POST_OF_ME
} from './user';
export const initialState = {
mainPosts: [],
imagePaths: [], // 이미지를 업로드 할때 이미지 경로
singlePost: null,
postAdded: false,
hasMorePosts: true,
likePostLoading: false,
likePostDone: false, // 추가
likePostError: null, // 추가
retweetLoading: false,
retweetDone: false, // 추가
retweetError: null, // 추가
uploadImagesLoading: false,
uploadImagesDone: false, // 추가
uploadImagesError: null, // 추가
unlikePostLoading: false,
unlikePostDone: false, // 추가
unlikePostError: null, // 추가
loadPostsLoading: false,
loadPostsDone: false, // 추가
loadPostsError: null, // 추가
loadPostLoading: false,
loadPostDone: false, // 추가
loadPostError: null, // 추가
// 게시글 추가가 완료되었을때 true 로 변한다. postAdded: false, => addPostLoading: false,
addPostLoading: false,
addPostDone: false, // 추가
addPostError: null, // 추가
addCommentLoading: false, // 댓글 추가가 완료되었을때 true 로 변한다.
addCommentDone: false, // 추가
addCommentError: null, // 추가
removePostLoading: false,
removePostDone: false, // 추가
removePostError: null, // 추가
};
export const UPLOAD_IMAGES_REQUEST = 'UPLOAD_IMAGES_REQUEST';
export const UPLOAD_IMAGES_SUCCESS = 'UPLOAD_IMAGES_SUCCESS';
export const UPLOAD_IMAGES_FAILURE = 'UPLOAD_IMAGES_FAILURE';
export const RETWEET_REQUEST = 'RETWEET_REQUEST';
export const RETWEET_SUCCESS = 'RETWEET_SUCCESS';
export const RETWEET_FAILURE = 'RETWEET_FAILURE';
export const LOAD_POSTS_REQUEST = 'LOAD_POSTS_REQUEST';
export const LOAD_POSTS_SUCCESS = 'LOAD_POSTS_SUCCESS';
export const LOAD_POSTS_FAILURE = 'LOAD_POSTS_FAILURE';
export const LOAD_POST_REQUEST = 'LOAD_POST_REQUEST';
export const LOAD_POST_SUCCESS = 'LOAD_POST_SUCCESS';
export const LOAD_POST_FAILURE = 'LOAD_POST_FAILURE';
export const LOAD_USER_POSTS_REQUEST = 'LOAD_USER_POSTS_REQUEST';
export const LOAD_USER_POSTS_SUCCESS = 'LOAD_USER_POSTS_SUCCESS';
export const LOAD_USER_POSTS_FAILURE = 'LOAD_USER_POSTS_FAILURE';
export const LOAD_HASHTAG_POSTS_REQUEST = 'LOAD_HASHTAG_POSTS_REQUEST';
export const LOAD_HASHTAG_POSTS_SUCCESS = 'LOAD_HASHTAG_POSTS_SUCCESS';
export const LOAD_HASHTAG_POSTS_FAILURE = 'LOAD_HASHTAG_POSTS_FAILURE';
export const LIKE_POST_REQUEST = 'LIKE_POST_REQUEST';
export const LIKE_POST_SUCCESS = 'LIKE_POST_SUCCESS';
export const LIKE_POST_FAILURE = 'LIKE_POST_FAILURE';
export const UNLIKE_POST_REQUEST = 'UNLIKE_POST_REQUEST';
export const UNLIKE_POST_SUCCESS = 'UNLIKE_POST_SUCCESS';
export const UNLIKE_POST_FAILURE = 'UNLIKE_POST_FAILURE';
export const ADD_POST_REQUEST = 'ADD_POST_REQUEST';
export const ADD_POST_SUCCESS = 'ADD_POST_SUCCESS';
export const ADD_POST_FAILURE = 'ADD_POST_FAILURE';
export const REMOVE_POST_REQUEST = 'REMOVE_POST_REQUEST';
export const REMOVE_POST_SUCCESS = 'REMOVE_POST_SUCCESS';
export const REMOVE_POST_FAILURE = 'REMOVE_POST_FAILURE';
export const ADD_COMMENT_REQUEST = 'ADD_COMMENT_REQUEST';
export const ADD_COMMENT_SUCCESS = 'ADD_COMMENT_SUCCESS';
export const ADD_COMMENT_FAILURE = 'ADD_COMMENT_FAILURE';
export const REMOVE_IMAGE = 'REMOVE_IMAGE';
export const addComment = (data) => ({
type: ADD_COMMENT_REQUEST,
data,
});
export const addPost = (data) => ({
type: ADD_POST_REQUEST,
data,
});
export const removePost = (data) => ({
type: REMOVE_POST_OF_ME,
data,
});
const reducer = (state = initialState, action) => produce(state, (draft) => {
switch (action.type) {
case REMOVE_IMAGE:
draft.imagePaths = draft.imagePaths.filter((v, i) => i !== action.data);
break;
case RETWEET_REQUEST:
draft.retweetLoading = true;
draft.retweetDone = false;
draft.retweetError = null;
break;
case RETWEET_SUCCESS:
draft.retweetLoading = false;
draft.retweetDone = true;
draft.mainPosts.unshift(action.data);
break;
case RETWEET_FAILURE:
draft.retweetLoading = false;
draft.retweetError = action.error;
break;
case UPLOAD_IMAGES_REQUEST:
draft.uploadImagesLoading = true;
draft.uploadImagesDone = false;
draft.uploadImagesError = null;
break;
case UPLOAD_IMAGES_SUCCESS: {
draft.imagePaths = action.data;
draft.uploadImagesLoading = false;
draft.uploadImagesDone = true;
break;
}
case UPLOAD_IMAGES_FAILURE:
draft.uploadImagesLoading = false;
draft.uploadImagesError = action.error;
break;
case LIKE_POST_REQUEST:
draft.likePostLoading = true;
draft.likePostDone = false;
draft.likePostError = null;
break;
case LIKE_POST_SUCCESS: {
const post = draft.mainPosts.find((v) => v.id === action.data.PostId);
post.Likers.push({ id: action.data.UserId });
draft.likePostLoading = false;
draft.likePostDone = true;
break;
}
case LIKE_POST_FAILURE:
draft.likePostLoading = false;
draft.likePostError = action.error;
break;
case UNLIKE_POST_REQUEST:
draft.unlikePostLoading = true;
draft.unlikePostDone = false;
draft.unlikePostError = null;
break;
case UNLIKE_POST_SUCCESS: {
console.log(action.data);
const post = draft.mainPosts.find((v) => v.id === action.data.PostId);
post.Likers = post.Likers.filter((v) => v.id !== action.data.UserId); // 제거
draft.unlikePostLoading = false;
draft.unlikePostDone = true;
break;
}
case UNLIKE_POST_FAILURE:
draft.unlikePostLoading = false;
draft.unlikePostError = action.error;
break;
case LOAD_USER_POSTS_REQUEST:
case LOAD_HASHTAG_POSTS_REQUEST:
case LOAD_POSTS_REQUEST:
draft.loadPostsLoading = true;
draft.loadPostsDone = false;
draft.loadPostsError = null;
break;
case LOAD_USER_POSTS_SUCCESS:
case LOAD_HASHTAG_POSTS_SUCCESS:
case LOAD_POSTS_SUCCESS:
draft.loadPostsLoading = false;
draft.loadPostsDone = true;
draft.mainPosts = draft.mainPosts.concat(action.data);
draft.hasMorePosts = action.data.length === 10;
break;
case LOAD_USER_POSTS_FAILURE:
case LOAD_HASHTAG_POSTS_FAILURE:
case LOAD_POSTS_FAILURE:
draft.loadPostsLoading = false;
draft.loadPostsError = action.error;
break;
case LOAD_POST_REQUEST:
draft.loadPostLoading = true;
draft.loadPostDone = false;
draft.loadPostError = null;
break;
case LOAD_POST_SUCCESS:
draft.loadPostLoading = false;
draft.loadPostDone = true;
draft.singlePost = action.data;
break;
case LOAD_POST_FAILURE:
draft.loadPostLoading = false;
draft.loadPostError = action.error;
break;
case ADD_POST_REQUEST:
draft.addPostLoading = true;
draft.addPostDone = false;
draft.addPostError = null;
break;
case ADD_POST_SUCCESS:
draft.addPostLoading = false;
draft.addPostDone = true;
draft.mainPosts.unshift(action.data);
draft.imagePaths = [];
break;
case ADD_POST_FAILURE:
draft.addPostLoading = true;
draft.addPostError = action.error;
break;
case ADD_COMMENT_REQUEST:
draft.addCommentLoading = true;
draft.addCommentDone = false;
draft.addCommentError = null;
break;
case ADD_COMMENT_SUCCESS:
// action.data.content, action.data.postId, action.data.userId
// post.Comments.unshift(dummyComment(action.data.content));
const post = draft.mainPosts.find((v) => v.id === action.data.PostId);
post.Comments.unshift(action.data);
draft.addCommentLoading = false;
draft.addCommentDone = true;
break;
case ADD_COMMENT_FAILURE:
draft.addCommentLoading = false;
draft.addCommentError = action.error;
break;
case REMOVE_POST_REQUEST:
draft.removePostLoading = true;
draft.removePostDone = false;
draft.removePostError = null;
break;
// [ !== 일치하는 게시글만 지운다. ] 그리고 [ === 일치하는 게시글 이외의 모든 게시글이 지워진다. ]
// filter를 사용해서 불변성을 유지한다.
case REMOVE_POST_SUCCESS:
draft.removePostLoading = false;
draft.removePostDone = true;
draft.mainPosts = draft.mainPosts.filter((v) => v.id !== action.data.PostId);
break;
case REMOVE_POST_FAILURE:
draft.removePostLoading = false;
draft.removePostError = action.error;
break;
default:
break;
}
});
export default reducer;
reducers/user.js
import produce from 'immer';
export const initialState = {
removeFollowerLoading: false, // 내가 수락한 친구 차단하기
removeFollowerDone: false, // 추가
removeFollowerError: null, // 추가
loadUserLoading: false, // 사용자 정보 가져오기 시도중.. 로딩창을 띄운다.
loadUserDone: false,
loadUserError: null,
loadMyInfoLoading: false, // 내 정보 가져오기 시도중.. 로딩창을 띄운다.
loadMyInfoDone: false,
loadMyInfoError: null,
logInLoading: false, // 로그인 시도중.. 로딩창을 띄운다.
logInDone: false,
logInError: null,
logOutLoading: false, // 로그아웃 시도중.. 로딩창을 띄운다.
logOutDone: false,
logOutError: null,
followLoading: false, // 팔로우 시도중.. 로딩창을 띄운다.
followDone: false,
followError: null,
unfollowLoading: false, // 언팔로우 시도중.. 로딩창을 띄운다.
unfollowDone: false,
unfollowError: null,
loadFollowersLoading: false, // 팔로워 목록 가져오기 시도중.. 로딩창을 띄운다.
loadFollowersDone: false,
loadFollowersError: null,
loadFollowingsLoading: false, // 팔로윙 목록 가져오기 시도중.. 로딩창을 띄운다.
loadFollowingsDone: false,
loadFollowingsError: null,
signUpLoading: false, // 회원가입 시도중.. 로딩창을 띄운다.
signUpDone: false,
signUpError: null,
changeNicknameLoading: false, // 닉네임 변경 시도중.. 로딩창을 띄운다.
changeNicknameDone: false,
changeNicknameError: null,
me: null,
userInfo: null,
};
export const REMOVE_FOLLOWER_REQUEST = 'REMOVE_FOLLOWER_REQUEST';
export const REMOVE_FOLLOWER_SUCCESS = 'REMOVE_FOLLOWER_SUCCESS';
export const REMOVE_FOLLOWER_FAILURE = 'REMOVE_FOLLOWER_FAILURE';
export const LOAD_MY_INFO_REQUEST = 'LOAD_MY_INFO_REQUEST';
export const LOAD_MY_INFO_SUCCESS = 'LOAD_MY_INFO_SUCCESS';
export const LOAD_MY_INFO_FAILURE = 'LOAD_MY_INFO_FAILURE';
export const LOAD_USER_REQUEST = 'LOAD_USER_REQUEST';
export const LOAD_USER_SUCCESS = 'LOAD_USER_SUCCESS';
export const LOAD_USER_FAILURE = 'LOAD_USER_FAILURE';
export const LOG_IN_REQUEST = 'LOG_IN_REQUEST';
export const LOG_IN_SUCCESS = 'LOG_IN_SUCCESS';
export const LOG_IN_FAILURE = 'LOG_IN_FAILURE';
export const LOG_OUT_REQUEST = 'LOG_OUT_REQUEST';
export const LOG_OUT_SUCCESS = 'LOG_OUT_SUCCESS';
export const LOG_OUT_FAILURE = 'LOG_OUT_FAILURE';
export const SIGN_UP_REQUEST = 'SIGN_UP_REQUEST';
export const SIGN_UP_SUCCESS = 'SIGN_UP_SUCCESS';
export const SIGN_UP_FAILURE = 'SIGN_UP_FAILURE';
export const CHANGE_NICKNAME_REQUEST = 'CHANGE_NICKNAME_REQUEST';
export const CHANGE_NICKNAME_SUCCESS = 'CHANGE_NICKNAME_SUCCESS';
export const CHANGE_NICKNAME_FAILURE = 'CHANGE_NICKNAME_FAILURE';
export const FOLLOW_REQUEST = 'FOLLOW_REQUEST';
export const FOLLOW_SUCCESS = 'FOLLOW_SUCCESS';
export const FOLLOW_FAILURE = 'FOLLOW_FAILURE';
export const UNFOLLOW_REQUEST = 'UNFOLLOW_REQUEST';
export const UNFOLLOW_SUCCESS = 'UNFOLLOW_SUCCESS';
export const UNFOLLOW_FAILURE = 'UNFOLLOW_FAILURE';
export const LOAD_FOLLOWERS_REQUEST = 'LOAD_FOLLOWERS_REQUEST';
export const LOAD_FOLLOWERS_SUCCESS = 'LOAD_FOLLOWERS_SUCCESS';
export const LOAD_FOLLOWERS_FAILURE = 'LOAD_FOLLOWERS_FAILURE';
export const LOAD_FOLLOWINGS_REQUEST = 'LOAD_FOLLOWINGS_REQUEST';
export const LOAD_FOLLOWINGS_SUCCESS = 'LOAD_FOLLOWINGS_SUCCESS';
export const LOAD_FOLLOWINGS_FAILURE = 'LOAD_FOLLOWINGS_FAILURE';
export const ADD_POST_TO_ME = 'ADD_POST_TO_ME';
export const REMOVE_POST_OF_ME = 'REMOVE_POST_OF_ME';
export const ADD_COMMENT_TO_ME = 'ADD_COMMENT_TO_ME';
export const REMOVE_COMMENT_OF_ME = 'REMOVE_COMMENT_OF_ME';
const reducer = (state = initialState, action) => produce (state, (draft) => {
switch (action.type) {
case REMOVE_FOLLOWER_REQUEST:
draft.removeFollowerLoading = true;
draft.removeFollowerDone = false;
draft.removeFollowerError = null;
break;
case REMOVE_FOLLOWER_SUCCESS: {
draft.removeFollowerLoading = false;
draft.me.Followers = draft.me.Followers.filter((v) => v.id !== action.data.UserId);
draft.removeFollowerDone = true;
break;
}
case REMOVE_FOLLOWER_FAILURE:
draft.removeFollowerLoading = false;
draft.removeFollowerError = action.error;
break;
case LOAD_MY_INFO_REQUEST:
draft.loadMyInfoLoading = true;
draft.loadMyInfoError = null;
draft.loadMyInfoDone = false;
break;
case LOAD_MY_INFO_SUCCESS:
draft.loadMyInfoLoading = false;
draft.me = action.data;
draft.loadMyInfoDone = true;
break;
case LOAD_MY_INFO_FAILURE:
draft.loadMyInfoLoading = false; // isLoggingOut: true, => logOutLoading: true,
draft.loadMyInfoError = action.error; // logInError: action.error, => logOutDone: false,
break;
case LOAD_USER_REQUEST:
draft.loadUserLoading = true;
draft.loadUserError = null;
draft.loadUserDone = false;
break;
case LOAD_USER_SUCCESS:
draft.loadUserLoading = false;
draft.userInfo = action.data;
draft.loadUserDone = true;
break;
case LOAD_USER_FAILURE:
draft.loadUserLoading = false; // isLoggingOut: true, => logOutLoading: true,
draft.loadUserError = action.error; // logInError: action.error, => logOutDone: false,
break;
case LOAD_FOLLOWERS_REQUEST:
draft.loadFollowersLoading = true;
draft.loadFollowersError = null;
draft.loadFollowersDone = false;
break;
case LOAD_FOLLOWERS_SUCCESS:
draft.loadFollowersLoading = false;
draft.me.Followers = action.data;
draft.loadFollowersDone = true;
break;
case LOAD_FOLLOWERS_FAILURE:
draft.loadFollowersLoading = false;
draft.loadFollowersError = action.error;
break;
case LOAD_FOLLOWINGS_REQUEST:
draft.loadFollowingsLoading = true;
draft.loadFollowingsError = null;
draft.loadFollowingsDone = false;
break;
case LOAD_FOLLOWINGS_SUCCESS:
draft.loadFollowingsLoading = false;
draft.me.Followings = action.data;
draft.loadFollowingsDone = true;
break;
case LOAD_FOLLOWINGS_FAILURE:
draft.loadFollowingsLoading = false;
draft.loadFollowingsError = action.error;
break;
case FOLLOW_REQUEST:
draft.followLoading = true;
draft.followError = null;
draft.followDone = false;
break;
case FOLLOW_SUCCESS:
draft.followLoading = false;
draft.me.Followings.push({ id: action.data.UserId });
draft.followDone = true;
break;
case FOLLOW_FAILURE:
draft.followLoading = false; // isLoggingOut: true, => logOutLoading: true,
draft.followError = action.error; // logInError: action.error, => logOutDone: false,
break;
case UNFOLLOW_REQUEST:
draft.unfollowLoading = true;
draft.unfollowError = null;
draft.unfollowDone = false;
break;
case UNFOLLOW_SUCCESS:
draft.unfollowLoading = false;
draft.unfollowDone = true;
draft.me.Followings = draft.me.Followings.filter((v) => v.id !== action.data.UserId );
break;
case UNFOLLOW_FAILURE:
draft.unfollowLoading = false; // isLoggingOut: true, => logOutLoading: true,
draft.unfollowError = action.error; // logInError: action.error, => logOutDone: false,
break;
case CHANGE_NICKNAME_REQUEST:
draft.changeNicknameLoading = true; // 닉네임 변경 시도중이니까 => 버튼 로딩 O
draft.changeNicknameDone = false; // 닉네임 변경중
draft.changeNicknameError = null;
break;
case CHANGE_NICKNAME_SUCCESS:
draft.me.nickname = action.data.nickname;
draft.changeNicknameLoading = false; // 닉네임 변경 요청이 성공했으니까 => 버튼 로딩 X
draft.changeNicknameDone = true; // 닉네임 변경 완료
break;
case CHANGE_NICKNAME_FAILURE:
draft.changeNicknameLoading = false; // 닉네임 변경 요청이 끝났으니까 => 버튼 로딩 X
draft.changeNicknameError = action.error;
break;
case SIGN_UP_REQUEST:
draft.signUpLoading = true; // 회원가입 시도중이니까 => true isLoggingOut: true, => logInLoading: true,
draft.signUpDone = false;
draft.signUpError = null;
break;
case SIGN_UP_SUCCESS:
draft.signUpLoading = false; // 회원가입 요청이 성공했으니까 => false isLoggingOut: false, => logOutLoading: false,
draft.signUpDone = true; // isLoggedIn: false, => logOutDone: true,
break;
case SIGN_UP_FAILURE:
draft.signUpLoading = false; // 회원가입 요청이 끝났으니까 => false isLoggingOut: false, => logOutLoading: false,
draft.signUpError = action.error; // 추가
break;
case LOG_IN_REQUEST:
draft.logInLoading = true;
draft.logInError = null;
draft.logInDone = false;
break;
case LOG_IN_SUCCESS:
draft.logInLoading = false;
draft.logInDone = true;
draft.me = action.data;
break;
case LOG_IN_FAILURE:
draft.logInLoading = false; // isLoggingOut: true, => logOutLoading: true,
draft.logInError = action.error; // logInError: action.error, => logOutDone: false,
break;
case LOG_OUT_REQUEST:
draft.logOutLoading = true; // 로그아웃 시도중이니까 => true
draft.logOutDone = false;
draft.logOutError = null;
break;
case LOG_OUT_SUCCESS:
draft.logOutLoading = false; // 로그아웃 요청이 성공했으니까 => false isLoggingOut: false, => logOutLoading: false,
draft.logOutDone = true; // isLoggedIn: false, => logOutDone: true,
draft.me = null;
break;
case LOG_OUT_FAILURE:
draft.isLoggingOut = false; // 요청이 끝났으니까 => false isLoggingOut: false, => logOutLoading: false,
draft.logOutError = action.error; // 추가
break;
case ADD_POST_TO_ME:
draft.me.Posts.unshift({ id: action.data });
break;
case REMOVE_POST_OF_ME:
draft.me.Posts = draft.me.Posts.filter((v) => v.id !== action.data);
break;
default:
break;
}
});
export default reducer;
답변 2
0
0




