웹 요청
1. Promise : ES6 문법에서 비동기 처리를 다루는 데 사용하는 객체
axios 라는 Promise 기반 HTTP 클라이언트 라이브러리 사용하여 웹 요청
2. redux-pender : Promise 기반 액션들을 관리하는 미들웨어가 포함되어 있는 라이브러리
yarn add axios redux redux-actions redux-logger redux-pender
스토어 생성
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux';
import store from './store';
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>,
document.getElementById('root')
);
스토어 설정
src/store.js
import { createStore, applyMiddleware } from 'redux';
import modules from './modules';
import { createLogger } from 'redux-logger';
import penderMiddleware from 'redux-pender';
/* 로그 미들웨어를 생성할 때 설정을 커스터마이징할 수 있습니다.
https://github.com/evgenyrodionov/redux-logger#options
*/
const logger = createLogger();
const store = createStore(modules, applyMiddleware(logger, penderMiddleware()))
export default store;
src/App.js
import React, {Component} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import * as counterActions from './modules/counter';
import * as postActions from './modules/post';
class App extends Component {
cancelRequest = null
handleCancel = () => {
if(this.cancelRequest) {
this.cancelRequest();
this.cancelRequest = null;
}
}
loadData = async () => {
const { PostActions, number } = this.props;
try {
const p = PostActions.getPost(number);
this.cancelRequest = p.cancel;
const response = await p;
console.log(response);
} catch (e) {
console.log(e);
}
}
componentDidMount() {
this.loadData();
window.addEventListener('keyup', (e) => {
if(e.key === 'Escape') {
this.handleCancel();
}
})
}
componentDidUpdate(prevProps, prevState) {
// 이전 number와 현재 number가 다르면 요청을 시작합니다
if(this.props.number !== prevProps.number) {
this.loadData();
}
}
render() {
const {CounterActions, number, post, error, loading} = this.props;
return (
<div>
<h1>{number}</h1>
{
(() => {
if(loading)
return (<h2>로딩중...</h2>);
if(error)
return (<h2>오류 발생!</h2>)
return (
<div>
<h2>{post.title}</h2>
<p>{post.body}</p>
</div>
)
})()
}
<button onClick={CounterActions.increment}>+</button>
<button onClick={CounterActions.decrement}>-</button>
</div>
);
}
}
// 이제 post 리듀서에서 error 값과 pending값을 더 이상 관여하지 않고,
// pender 리듀서가 대신 하게 된다., App 컴포넌트에서 마지막 connect하는 부분만 수정
// loading : state.pender.pending -> state.pender.pending['GET_POST'] 으로 수정
export default connect(
(state) => ({
number: state.counter,
post: state.post.data,
loading: state.pender.pending['GET_POST'],
error: state.pender.failure['GET_POST']
}),
(dispatch) => ({
CounterActions: bindActionCreators(counterActions, dispatch),
PostActions: bindActionCreators(postActions, dispatch)
})
)(App);
src/modules/counter.js
import { handleActions, createAction } from 'redux-actions';
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
export const increment = createAction(INCREMENT);
export const decrement = createAction(DECREMENT);
export const incrementAsync = () => dispatch => {
// 1초 뒤 액션 디스패치
setTimeout(
() => { dispatch(increment()) },
1000
);
}
export const decrementAsync = () => dispatch => {
// 1초 뒤 액션 디스패치
setTimeout(
() => { dispatch(decrement()) },
1000
);
}
export default handleActions({
[INCREMENT]: (state, action) => state + 1,
[DECREMENT]: (state, action) => state - 1
}, 1);
src/modules/post.js
import {handleActions, createAction} from 'redux-actions';
import { applyPenders } from 'redux-pender';
import axios from 'axios';
function getPostAPI(postId) {
return axios.get(`https://jsonplaceholder.typicode.com/posts/${postId}`)
}
const GET_POST = 'GET_POST';
/* redux-pender의 액션 구조는 Flux standard actionㅇ을 따르기 떄문에,
createAction으로 액션을 만들 수 있습니다. 두 번째로 들억나느 파라미터는 Promise를 반환하는 함수
*/
export const getPost = createAction(GET_POST, getPostAPI);
const initialState = {
data: {
title: '',
body: ''
}
}
const reducer = handleActions({
// 다른 일반 액션들을 관리..
}, initialState);
// applyPenders 함수를 사용할 때 첫번째 파라미터에는 일반 리듀서를 넣어주고,
// 두번째 파라미터에는 pender 관련 객체들을 배열 형태로 넣어 준다.
export default applyPenders(reducer, [
{
type: GET_POST,
onSuccess: (state, action) => {
// 성공했을 때 해야 할 작업이 따로 없으면 이 함수 또한 생략해도 됩니다.
const { title, body } = action.payload.data;
return {
data: {
title,
body
}
}
},
onCancel: (state, action) => {
return {
data: {
title: '취소됨',
body: '취소됨'
}
}
}
},
/*
다른 pender 액션들
{ type: GET_SOMETHING, onSuccess: (state, action) => ... },
{ type: GET_SOMETHING, onSuccess: (state, action) => ... }
*/
]);
리듀서 설정 [합치기]
src/modules/index.js
import { combineReducers } from 'redux';
import counter from './counter';
import post from './post';
import { penderReducer } from 'redux-pender';
export default combineReducers({
counter,
post,
pender: penderReducer
});
결과물)
반응형
'WEB > REACT' 카테고리의 다른 글
13. 코드 스플리팅 (0) | 2019.07.13 |
---|---|
12. react-router로 SPA (0) | 2019.07.12 |
10. 미들웨어 (0) | 2019.07.11 |
9. Immutable & Ducks & 예제 (0) | 2019.07.11 |
8. 리덕스 활용 예제 (0) | 2019.07.09 |