August 12, 2019
참조
개인적으로 진행하던 익명 투표 시스템의 비동기 부분을 담당하던
redux-thunk를 redux-saga로 교체한 코드들에 대한 변경점을 공유하고자 한다.
|-- actions
|-- ActionType.js
|-- index.js
|-- store
|-- index.js
//store index.js
import { createStore, applyMiddleware } from 'redux';
import rootReducer from '../reducers/index';
import thunk from 'redux-thunk'
export default function configureStore(initialState={}) {
let log = [];
if(
window.location &&
window.location.host &&
window.location.host.indexOf('localhost') !== -1){
const { logger } = require("redux-logger");
log.push(logger);
}
return createStore(
rootReducer,
initialState,
applyMiddleware(thunk, ...log)
);
}
// actions index.js
import * as types from './ActionTypes'
export const fetchDataStart = () => {
return{
type: types.FETCH_DATA_START
}
};
export const fetchDataSuccess = data => {
return{
type: types.FETCH_DATA_SUCCESS,
payload: { data }
}
};
export const fetchDataFailure = error => {
return{
type: types.FETCH_DATA_FAILURE,
payload: { error }
}
};
export const fetchData = (method = 'GET', data, path = '') => {
let id = "";
if(method === 'PUT' || method === 'DELETE') id = data && data.id;
let header = {'Content-Type':'application/json', 'Accept': 'application/json'};
if(data && method !== 'DELETE'){
header = {'Content-Type':'application/x-www-form-urlencoded'}
}
const searchParams = (params) => {
return Object.keys(params).map((key) => {
if(key === 'contents') return encodeURIComponent(key) + '=' + encodeURIComponent(JSON.stringify(params[key]));
else return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
}).join('&');
}
return dispatch => {
dispatch(fetchDataStart());
return fetch(`/api/votes/${path}${id}`, {
headers : header,
method: method && method,
body: data && method !== 'DELETE' ? searchParams(data) : undefined
})
.then(handleErrors)
.then(res => {
return res.json()
})
.then(json => {
dispatch(fetchDataSuccess(json));
})
.catch(error => dispatch(fetchDataFailure(error)));
};
}
const handleErrors = (response) => {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
export const getHeaderItems = (headerType) => {
return{
type:types.HEADER_TYPE,
headerType: headerType
}
}
|-- actions
|-- ActionType.js
|-- sagas
|-- index.js
|-- store
|-- configureStore.dev.js
|-- configureStore.js
|-- configureStore.prod.js
// sagas index.js
import * as types from '../actions/ActionTypes'
import { all, put, takeEvery } from 'redux-saga/effects'
function* fetchDataStart() {
yield takeEvery(types.FETCH_DATA_START, fetching);
}
function* fetchDataSuccess(data) {
yield put({
type: types.FETCH_DATA_SUCCESS,
payload: { data }
})
}
function* fetchDataFailure(error) {
yield put({
type: types.FETCH_DATA_FAILURE,
payload: { error }
})
}
function* fetching(action) {
try {
const data = yield fetchData(action.payload.method, action.payload.data, action.payload.path);
yield fetchDataSuccess(data)
} catch (error) {
yield fetchDataFailure(error)
}
}
export default function* rootSaga() {
yield all([
fetchDataStart(),
])
}
const fetchData = (method = 'GET', data, path = '') => {
let id = "";
if(method === 'PUT' || method === 'DELETE') id = data && data.id;
let header = {'Content-Type':'application/json', 'Accept': 'application/json'};
if(data && method !== 'DELETE'){
header = {'Content-Type':'application/x-www-form-urlencoded'}
}
const searchParams = (params) => {
return Object.keys(params).map((key) => {
if(key === 'contents') return encodeURIComponent(key) + '=' + encodeURIComponent(JSON.stringify(params[key]));
else return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
}).join('&');
}
return fetch(`/api/votes/${path}${id}`, {
headers : header,
method: method && method,
body: data && method !== 'DELETE' ? searchParams(data) : undefined
})
.then(handleErrors)
.then(res => {
return res.json()
})
.catch(error => {
return error
});
}
const handleErrors = (response) => {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
// store configureStore.dev.js
import { createStore, applyMiddleware, compose } from 'redux'
import { createLogger } from 'redux-logger'
import createSagaMiddleware, { END } from 'redux-saga'
import sagaMonitor from '@redux-saga/simple-saga-monitor'
import DevTools from '../containers/DevTools'
import rootReducer from '../reducers'
export default function configureStore(initialState) {
const sagaMiddleware = createSagaMiddleware({ sagaMonitor })
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(sagaMiddleware, createLogger()),
DevTools.instrument(),
),
)
if (module.hot) {
module.hot.accept('../reducers', () => {
const nextRootReducer = require('../reducers').default
store.replaceReducer(nextRootReducer)
})
}
store.runSaga = sagaMiddleware.run
store.close = () => store.dispatch(END)
return store
}
// store configureStore.prod.js
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware, { END } from 'redux-saga'
import sagaMonitor from '@redux-saga/simple-saga-monitor'
import rootReducer from '../reducers'
export default function configureStore(initialState) {
const sagaMiddleware = createSagaMiddleware({ sagaMonitor })
const store = createStore(rootReducer, initialState, applyMiddleware(sagaMiddleware))
store.runSaga = sagaMiddleware.run
store.close = () => store.dispatch(END)
return store
}
// store configureStore.js
if (process.env.NODE_ENV === 'production') {
module.exports = require('./configureStore.prod')
} else {
module.exports = require('./configureStore.dev')
}