Redux-saga๋ ํ๋ก์ ํธ ๊ท๋ชจ๊ฐ ์ปค์ง์๋ก ์์ฑํด์ผ ํ๋ ์ฝ๋์ ์์ด ๋ง๊ณ ๊ทธ ๋์์๋ฆฌ๊ฐ ๊ทผ๋ณธ์ ์ผ๋ก ๋ณต์กํฉ๋๋ค.
๋ฐ๋ผ์ Redux-pender๋ฅผ ์ด์ฉํด ์ ์ญ ์ํ๊ด๋ฆฌ๋ฅผ ํ๋ ค ํฉ๋๋ค.
import { createAction, handleActions } from 'redux-actions';
import createRequestSaga, {
createRequestActionTypes,
} from '../lib/util/createRequestSaga';
import * as chatAPI from '../lib/api/chat';
import { call, put, takeLatest, throttle } from 'redux-saga/effects';
const INITIALIZE = 'chat/INITIALIZE';
const [
GET_CHANNEL_MESSAGES,
GET_CHANNEL_MESSAGES_SUCCESS,
GET_CHANNEL_MESSAGES_FAILURE,
] = createRequestActionTypes('chat/GET_CHANNEL_MESSAGES');
const [
SEND_CHANNEL_MESSAGE,
SEND_CHANNEL_MESSAGE_SUCCESS,
SEND_CHANNEL_MESSAGE_FAILURE,
] = createRequestActionTypes('chat/SEND_CHANNEL_MESSAGE');
const CONCAT_CHANNEL_MESSAGES = 'chat/CONCAT_CHANNEL_MESSAGES';
const [
GET_ROOM_MESSAGES,
GET_ROOM_MESSAGES_SUCCESS,
GET_ROOM_MESSAGES_FAILURE,
] = createRequestActionTypes('chat/GET_ROOM_MESSAGES');
const [
SEND_ROOM_MESSAGE,
SEND_ROOM_MESSAGE_SUCCESS,
SEND_ROOM_MESSAGE_FAILURE,
] = createRequestActionTypes('chat/SEND_ROOM_MESSAGE');
const [
REPLY_ROOM_MESSAGE,
REPLY_ROOM_MESSAGE_SUCCESS,
REPLY_ROOM_MESSAGE_FAILURE,
] = createRequestActionTypes('chat/REPLY_ROOM_MESSAGE');
const CONCAT_ROOM_MESSAGES = 'chat/CONCAT_ROOM_MESSAGES';
export const initialize = createAction(INITIALIZE);
export const getChannelMessages = createAction(
GET_CHANNEL_MESSAGES,
({ channelId, lastId }) => ({ channelId, lastId }),
);
export const sendChannelMessage = createAction(
SEND_CHANNEL_MESSAGE,
({ channelId, content }) => ({ channelId, content }),
);
export const concatChannelMessages = createAction(
CONCAT_CHANNEL_MESSAGES,
(message) => message,
);
export const getRoomMessages = createAction(
GET_ROOM_MESSAGES,
({ roomId, lastId }) => ({ roomId, lastId }),
);
export const sendRoomMessage = createAction(
SEND_ROOM_MESSAGE,
({ roomId, content }) => ({ roomId, content }),
);
export const replyRoomMessage = createAction(
REPLY_ROOM_MESSAGE,
({ roomId, answeredId, content }) => ({ roomId, answeredId, content }),
);
export const concatRoomMessages = createAction(
CONCAT_ROOM_MESSAGES,
(message) => message,
);
export function* getChannelMessagesSaga(action) {
try {
const response = yield call(chatAPI.getChannelMessages, action.payload);
if (response.data.length > 0) {
yield put({
type: GET_CHANNEL_MESSAGES_SUCCESS,
payload: response.data,
meta: response,
});
}
} catch (e) {
yield put({
type: GET_CHANNEL_MESSAGES_FAILURE,
payload: e,
error: true,
});
}
}
const sendChannelMessageSaga = createRequestSaga(
SEND_CHANNEL_MESSAGE,
chatAPI.sendChannelMessage,
);
export function* getRoomMessagesSaga(action) {
try {
const response = yield call(chatAPI.getRoomMessages, action.payload);
if (response.data.length > 0) {
yield put({
type: GET_ROOM_MESSAGES_SUCCESS,
payload: response.data,
meta: response,
});
}
} catch (e) {
yield put({
type: GET_ROOM_MESSAGES_FAILURE,
payload: e,
error: true,
});
}
}
const sendRoomMessageSaga = createRequestSaga(
SEND_ROOM_MESSAGE,
chatAPI.sendRoomMessage,
);
const sendReplyMessageSaga = createRequestSaga(
REPLY_ROOM_MESSAGE,
chatAPI.replyRoomMessage,
);
export function* chatSaga() {
yield throttle(5000, GET_CHANNEL_MESSAGES, getChannelMessagesSaga);
yield takeLatest(SEND_CHANNEL_MESSAGE, sendChannelMessageSaga);
yield throttle(5000, GET_ROOM_MESSAGES, getRoomMessagesSaga);
yield takeLatest(SEND_ROOM_MESSAGE, sendRoomMessageSaga);
yield takeLatest(REPLY_ROOM_MESSAGE, sendReplyMessageSaga);
}
const initialState = {
messages: [],
lastId: null,
error: null,
success: null,
};
const chat = handleActions(
{
[GET_CHANNEL_MESSAGES_SUCCESS]: (state, { payload }) => ({
...state,
messages: state.messages.concat(payload),
lastId: payload[payload.length - 1].id,
}),
[GET_CHANNEL_MESSAGES_FAILURE]: (state, { payload: error }) => ({
...state,
error,
}),
[CONCAT_CHANNEL_MESSAGES]: (state, { payload: message }) => ({
...state,
messages: [message, ...state.messages],
}),
[GET_ROOM_MESSAGES_SUCCESS]: (state, { payload }) => ({
...state,
messages: state.messages.concat(payload),
lastId: payload[payload.length - 1].id,
}),
[GET_ROOM_MESSAGES_FAILURE]: (state, { payload: error }) => ({
...state,
error,
}),
[CONCAT_ROOM_MESSAGES]: (state, { payload: message }) => ({
...state,
messages: [message, ...state.messages],
}),
[SEND_CHANNEL_MESSAGE]: (state) => ({
...state,
success: null,
}),
[SEND_CHANNEL_MESSAGE_SUCCESS]: (state) => ({
...state,
success: true,
}),
[SEND_ROOM_MESSAGE]: (state) => ({
...state,
success: null,
}),
[SEND_ROOM_MESSAGE_SUCCESS]: (state) => ({
...state,
success: true,
}),
[REPLY_ROOM_MESSAGE]: (state) => ({
...state,
success: null,
}),
[REPLY_ROOM_MESSAGE_SUCCESS]: (state) => ({
...state,
success: true,
}),
[INITIALIZE]: () => initialState,
},
initialState,
);
export default chat;
import { createAction, handleActions } from 'redux-actions';
import { Map, List, fromJS } from 'immutable';
import { pender } from 'redux-pender';
import * as WebAPI from 'lib/web-api';
// ์ก์
ํ์
const CREATE_MEMO = 'memo/CREATE_MEMO';
const GET_INITIAL_MEMO = 'memo/GET_INITIAL_MEMO';
const GET_RECENT_MEMO = 'memo/GET_RECENT_MEMO';
const UPDATE_MEMO = 'memo/UPDATE_MEMO';
const DELETE_MEMO = 'memo/DELETE_MEMO';
const GET_PREVIOUS_MEMO = 'memo/GET_PREVIOUS_MEMO';
// ์ก์
์์ฑ์
export const createMemo = createAction(CREATE_MEMO, WebAPI.createMemo) // { title, body }
export const getInitialMemo = createAction(GET_INITIAL_MEMO, WebAPI.getInitialMemo);
export const getRecentMemo = createAction(GET_RECENT_MEMO, WebAPI.getRecentMemo) // cursor
export const updateMemo = createAction(UPDATE_MEMO, WebAPI.updateMemo, payload => payload); // { id, memo: {title,body} }
export const deleteMemo = createAction(DELETE_MEMO, WebAPI.deleteMemo, payload => payload); // id
export const getPreviousMemo = createAction(GET_PREVIOUS_MEMO, WebAPI.getPreviousMemo); // endCursor
const initialState = Map({
data: List()
});
export default handleActions({
// ์ด๊ธฐ ๋ฉ๋ชจ ๋ก๋ฉ
...pender({
type: GET_INITIAL_MEMO,
onSuccess: (state, action) => state.set('data', fromJS(action.payload.data))
}),
// ์ ๊ท ๋ฉ๋ชจ ๋ก๋ฉ
...pender({
type: GET_RECENT_MEMO,
onSuccess: (state, action) => {
// ๋ฐ์ดํฐ ๋ฆฌ์คํธ์ ์๋ถ๋ถ์ ์ ๋ฐ์ดํฐ๋ฅผ ๋ถ์ฌ์ค๋ค
const data = state.get('data');
return state.set('data', fromJS(action.payload.data).concat(data))
}
}),
...pender({
type: UPDATE_MEMO,
onSuccess: (state, action) => {
const { id, memo: { title, body } } = action.meta;
const index = state.get('data').findIndex(memo => memo.get('id') === id);
return state.updateIn(['data', index], (memo) => memo.merge({
title,
body
}))
}
}),
// ๋ฉ๋ชจ ์ญ์
...pender({
type: DELETE_MEMO,
onSuccess: (state, action) => {
const id = action.meta;
const index = state.get('data').findIndex(memo => memo.get('id') === id);
return state.deleteIn(['data', index]);
}
}),
...pender({
type: GET_PREVIOUS_MEMO,
onSuccess: (state, action) => {
// ๋ฐ์ดํฐ ๋ฆฌ์คํธ์ ๋ท๋ถ๋ถ์ ์ ๋ฐ์ดํฐ๋ฅผ ๋ถ์ฌ์ค๋ค
const data = state.get('data');
return state.set('data', data.concat(fromJS(action.payload.data)))
}
})
}, initialState);
pender๋ฅผ ์ด์ฉํธ ๋ชจ๋์ ์ฝ๋๋ฅผ ์์ธํ ๋ณด๋ฉด, ์ก์
ํ์
์ ๊ฐ๋จํ ์์ฑํ ์ ์๊ณ , sagaํจ์์ ์ก์
์์ฑํจ์๋ฅผ ๋ณต์กํ๊ฒ ์์ฑํ ํ์ ์์ด ๊ฐ๋จํฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ ๋ฆฌ๋์์์ ํ์
๋ณ๋ก ์ํ ์
๋ฐ์ดํธ๋ฅผ ๊ด๋ฆฌํด์ค ์ ์๊ธฐ ๋๋ฌธ์ ์ก์
์ด ๋ง์์ ๋ ๋ฆฌ๋์๊ฐ ๋ณด๊ธฐ ํธํด์ง๋๋ค.