import produce from 'immer'
export const initialState = {
mainPosts: [],
singlePost: null,
imagePaths: [],
hasMorePosts: true,
likePostLoading: false,
likePostDone: false,
likePostError: null,
unlikePostLoading: false,
unlikePostDone: false,
unlikePostError: null,
loadPostLoading: false,
loadPostDone: false,
loadPostError: null,
loadPostsLoading: false,
loadPostsDone: false,
loadPostsError: null,
addPostLoading: false,
addPostDone: false,
addPostError: null,
removePostLoading: false,
removePostDone: false,
removePostError: null,
addCommentLoading: false,
addCommentDone: false,
addCommentError: null,
uploadImagesLoading: false,
uploadImagesDone: false,
uploadImagesError: null,
retweetLoading: false,
retweetDone: false,
retweetError: 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 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 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 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 RETWEET_REQUEST = 'RETWEET_REQUEST'
export const RETWEET_SUCCESS = 'RETWEET_SUCCESS'
export const RETWEET_FAILURE = 'RETWEET_FAILURE'
export const REMOVE_IMAGE = 'REMOVE_IMAGE'
export const addPost = (data) => ({
type: ADD_POST_REQUEST,
data
})
export const addComment = (data) => ({
type: ADD_COMMENT_REQUEST,
data
})
const reducer = (state = initialState, action) => {
return 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: {
const post = draft.mainPosts.find((v) => v.id === action.data.PostId)
post.Likers = post.Likers.filter((v) => v.id !== action.data.UserId)
draft.likePostLoading = false
draft.likePostDone = true
break
}
case UNLIKE_POST_FAILURE:
draft.unlikePostLoading = false
draft.unlikePostError = action.error
break
case LOAD_POST_REQUEST:
draft.loadPostLoading = true
draft.loadPostDone = false
draft.loadPostError = null
break
case LOAD_POST_SUCCESS:
draft.singlePost = action.data
draft.loadPostLoading = false
draft.loadPostDone = true
break
case LOAD_POST_FAILURE:
draft.loadPostLoading = false
draft.loadPostError = action.error
break
case LOAD_POSTS_REQUEST:
draft.loadPostsLoading = true
draft.loadPostsDone = false
draft.loadPostsError = null
break
case LOAD_POSTS_SUCCESS:
draft.mainPosts = draft.mainPosts.concat(action.data)
draft.loadPostsLoading = false
draft.loadPostsDone = true
draft.hasMorePosts = action.data.length === 10
break
case LOAD_POSTS_FAILURE:
draft.loadPostsLoading = false
draft.loadPostsError = action.error
break
case ADD_POST_REQUEST:
draft.addPostLoading = true
draft.addPostDone = false
draft.addPostError = null
break
case ADD_POST_SUCCESS:
draft.mainPosts.unshift(action.data)
draft.addPostLoading = false
draft.addPostDone = true
draft.imagePaths = []
break
case ADD_POST_FAILURE:
draft.addPostLoading = false
draft.addPostError = action.error
break
case REMOVE_POST_REQUEST:
draft.removePostLoading = true
draft.removePostDone = false
draft.removePostError = null
break
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
case ADD_COMMENT_REQUEST:
draft.addCommentLoading = true
draft.addCommentDone = false
draft.addCommentError = null
break
case ADD_COMMENT_SUCCESS: {
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
default:
break
}
})
}
export default reducer
sagas/ post.js
import axios from 'axios'
import { all, delay, put, takeLatest, fork, throttle, call } from "redux-saga/effects";
import shortId from 'shortid';
import {
ADD_POST_FAILURE, ADD_POST_SUCCESS, ADD_COMMENT_SUCCESS,
ADD_COMMENT_FAILURE, ADD_POST_REQUEST, ADD_COMMENT_REQUEST,
REMOVE_POST_FAILURE, REMOVE_POST_REQUEST, REMOVE_POST_SUCCESS,
LOAD_POSTS_REQUEST, LOAD_POSTS_SUCCESS, LOAD_POSTS_FAILURE,
LIKE_POST_REQUEST, UNLIKE_POST_REQUEST, LIKE_POST_SUCCESS,
LIKE_POST_FAILURE, UNLIKE_POST_SUCCESS, UNLIKE_POST_FAILURE,
UPLOAD_IMAGES_REQUEST, UPLOAD_IMAGES_SUCCESS, UPLOAD_IMAGES_FAILURE,
RETWEET_REQUEST, RETWEET_SUCCESS, RETWEET_FAILURE,
LOAD_POST_REQUEST, LOAD_POST_SUCCESS, LOAD_POST_FAILURE
} from '../reducers/post'
import { ADD_POST_TO_ME, REMOVE_POST_OF_ME } from '../reducers/user';
function addPostAPI(data) {
return axios.post('/post', data)
}
function* addPost(action) {
try {
const result = yield call(addPostAPI, action.data)
const id = shortId.generate()
yield put({
type: ADD_POST_SUCCESS,
data: result.data
})
yield put({
type: ADD_POST_TO_ME,
data: result.data.id
})
} catch (err) {
yield put({
type: ADD_POST_FAILURE,
error: err.response.data
})
}
}
function retweetAPI(data) {
return axios.post(`/post/${data}/retweet`, data)
}
function* retweet(action) {
try {
const result = yield call(retweetAPI, action.data)
yield put({
type: RETWEET_SUCCESS,
data: result.data
})
} catch (err) {
yield put({
type: RETWEET_FAILURE,
error: err.response.data
})
}
}
function uploadImagesAPI(data) {
return axios.post(`/post/images`, data)
}
function* uploadImages(action) {
try {
const result = yield call(uploadImagesAPI, action.data)
yield put({
type: UPLOAD_IMAGES_SUCCESS,
data: result.data
})
} catch (err) {
yield put({
type: UPLOAD_IMAGES_FAILURE,
error: err.response.data
})
}
}
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) {
yield put({
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) {
yield put({
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
})
} catch (err) {
yield put({
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
})
} catch (err) {
yield put({
type: LOAD_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
})
yield put({
type: REMOVE_POST_OF_ME,
data: action.data
})
} catch (err) {
yield put({
type: REMOVE_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 put({
type: ADD_COMMENT_SUCCESS,
data: result.data
})
} catch (err) {
console.error(error)
yield put({
type: ADD_COMMENT_FAILURE,
error: err.response.data
})
}
}
function* watchRetweet() {
yield takeLatest(RETWEET_REQUEST, retweet)
}
function* watchUploadImages() {
yield takeLatest(UPLOAD_IMAGES_REQUEST, uploadImages)
}
function* watchLikePost() {
yield takeLatest(LIKE_POST_REQUEST, likePost)
}
function* watchUnlikePost() {
yield takeLatest(UNLIKE_POST_REQUEST, unlikePost)
}
function* watchAddPost() {
yield takeLatest(ADD_POST_REQUEST, addPost)
}
function* watchLoadPosts() {
yield throttle(5000, LOAD_POSTS_REQUEST, loadPosts)
}
function* watchLoadPost() {
yield takeLatest(LOAD_POST_REQUEST, loadPost)
}
function* watchRemovePost() {
yield takeLatest(REMOVE_POST_REQUEST, removePost)
}
function* watchAddComment() {
yield takeLatest(ADD_COMMENT_REQUEST, addComment)
}
export default function* postSaga() {
yield all([
fork(watchRetweet),
fork(watchUploadImages),
fork(watchLikePost),
fork(watchUnlikePost),
fork(watchAddPost),
fork(watchLoadPosts),
fork(watchLoadPost),
fork(watchRemovePost),
fork(watchAddComment)
])
}
pages/post/ [id].js
import axios from "axios"
import { useRouter } from "next/router"
import { useSelector } from "react-redux"
import { END } from 'redux-saga'
import AppLayout from "../../components/AppLayout"
import PostCard from "../../components/PostCard"
import { LOAD_POST_REQUEST } from "../../reducers/post"
import { LOAD_MY_INFO_REQUEST } from "../../reducers/user"
import wrapper from "../../store/configureStore"
const Post = () => {
const router = useRouter()
const { id } = router.query
const { singlePost } = useSelector((state) => state.post)
return (
<AppLayout>
<PostCard post={singlePost} />
</AppLayout>
)
}
export const getServerSideProps = wrapper.getServerSideProps(async (context) => {
const cookie = context.req ? context.req.headers.cookie : '';
console.log(context)
axios.defaults.headers.Cookie = '';
if (context.req && cookie) {
axios.defaults.headers.Cookie = cookie
}
context.store.dispatch({
type: LOAD_MY_INFO_REQUEST
})
context.store.dispatch({
type: LOAD_POST_REQUEST,
data: context.params.id
})
context.store.dispatch(END)
await context.store.sagaTask.toPromise()
})
export default Post
입니다