이전에 작성한 글들은 동기화 action입니다. action이 되면 바로 state에 반영되어서 적용이 되었습니다. 하지만 비동기인 상황에서는 대기시간이 걸립니다. 예로들어 API를 이용하여 데이터를 fetch하는 경우 입니다. 통신 상태에 따라 시간이 불규칙적으로 걸릴수 있기 때문입니다.
이번에는 axios 또는 fetch을 이용하여 코드를 짜보도록 하겠습니다.
일단 fetch을 하여 최종적으로 redux storage에 저장하는 것이 목적입니다. 그러기 위해서는 가장먼저 state를 설계해야 합니다. 내용과 구조는 아래와 같습니다.
loading : API를 통해서 데이터를 다운로드 받고 있는 중일때 true, 완료 혹은 error는 false
data : fetch(혹은 axios)를 통해 받은 데이터를 저장(여기서는 유저정보)
error: fetch도중 에러가 발생시 error메세지 저장
State = {
loading : true,
data : [ ],
error: ''
}
이제 action.type을 결정해야 합니다. 여기서 설정할수있는 action은 3가지 입니다. 내용은 아래와 같습니다.
FETCH_USERS_REQUEST : users 리스트를 fetch합니다.
FETCH_USERS_SUCCESS : fetch가 성공적으로 마무리 됬습니다.
FETCH_USERS_FAILURE : fetch도중 error가 발생.
이제 reducer를 설정해야 하는데 위의 action을 이용하여 설정할수 있습니다.
action.type
FETCH_USERS_REQUEST
- loading : true
FETCH_USERS_SUCCESS
- loading : false
- users : data (해당 API를 통해 다운)
FETCH_USERS_FAILURE
- loading : false
- err : 해당 err메세지
이제 코드를 작성하도록 하겠습니다.
// 초기 state
const initialState = {
loading: false,
users: [],
error: ''
}
일단 초기 stat을 선언합니다.
// action.type 설정
const FETCH_USERS_REQUEST = 'FETCH_USERS_REQUEST';
const FETCH_USERS_SUCCESS = 'FETCH_USERS_SUCCESS';
const FETCH_USERS_FAILURE = 'FETCH_USERS_FAILURE';
// fetch 요청
const fetchUsersRequest = () => {
return {
type : FETCH_USERS_REQUEST
}
};
// fetch 성공
const fetchUsersSuccess = users => {
return {
type : FETCH_USERS_SUCCESS,
payload : users
}
};
// fetch 실패
const fetchUsersFailure = error => {
return {
type : FETCH_USERS_FAILURE,
payload : error
}
};
이제 action.type을 설정합니다.
// reducer 선언
const reducer = (state = initialState, action) => {
switch(action.type){
// fetch요청
case FETCH_USERS_REQUEST:
return{
...state,
loading:true // 요청중일때는 true
};
// fetch성공
case FETCH_USERS_SUCCESS:
return {
loading: false, // 요청 완료후 false
users: action.payload, // 데이터 Update
error : ''
}
// fetch 실패
case FETCH_USERS_FAILURE:
return {
loading: false, // 에러 발생시 false
users: [], // 데이터 없음, 빈 배열
error: action.payload // 에러 메세지 Update
};
}
};
이제 reducer를 코딩합니다.
const redux = require('redux');
const createStore = redux.createStore;
.....
// storage생성
const storage = createStore(reducer);
가장 위쪽에 redux, createStore를 생성하고 마지막 줄에 createStore를 이용하여 storage를 만들어 줍니다.
이제 비동기 action을 만들어줘야 합니다. 여기서 2가지를 알아야 합니다.
axios, node-fetch : API 요청
redux-thunk : 비동기 action creators을 정의, middleware
고로 위 2개(node-fetch까지 테스트시 3개)를 설치해야 합니다.
// middleware 생성
const applyMiddleware = redux.applyMiddleware;
// redux-thunk 생성
const thunkMiddleware = require('redux-thunk').default;
// axios 생성
const axios = require('axios');
// node-fetch 생성
const fetch = require('node-fetch');
이제 middleware, redux-thunk, axios, node-fetch을 가장 위쪽에 생성해 줍니다.
// use jsonplaceholder
const fetchUsers = () => {
return function(dispatch) {
// dispatch을 통해서 reducer의 fetch요청 case실행
dispatch(fetchUsersRequest());
// url에 접속해서 Data를 얻는다.
axios.get('https://jsonplaceholder.typicode.com/users')
.then(response => {
// 데이터
const users = response.data.map(user => user.id);
// dispatch을 통해서 reducer의 fetch성공 case실행
dispatch(fetchUsersSuccess(users));
})
.catch(err => {
// dispatch을 통해서 reducer의 fetch실패 case실행
dispatch(fetchUsersFailure(err.message));
})
}
}
이제 fetchUsers함수를 생성하여 API요청을 합니다.
const storage = createStore(reducer, applyMiddleware(thunkMiddleware));
applyMiddleware를 생성했으니 createStore의 두번째 인자로 넣을수 있습니다. 이때 applyMiddleware의 인자는 thunkMiddleware입니다.
storage.subscribe(() => { console.log(store.getState())});
storage.dispatch(fetchUsers());
이제 storage.subscribe의 에로펑션을 넣어서 작성을 하고 fetchUser()을 dispatch해 줍니다.
이제 실행을 하면 위와 같이 terminal에 출력이 됩니다. 위 사항은 정상 fetch가 되었을때 terminal모습 입니다.
위의 terminal은 잘못된 url로 fetch을 시도 했을때 발생하는 에러 및 에러 메세지 입니다. axios대신 fetch을 사용하고 싶을때 아래 코드를 참고하여 주시기 바랍니다.
이전글 : Redux - Middleware(미들웨어)
이후글 : React-Redux_기초1 React생성, Redux 폴더 생성, dependencies설치
위 자료와 관련된 코드
const redux = require('redux');
const createStore = redux.createStore;
const applyMiddleware = redux.applyMiddleware;
const thunkMiddleware = require('redux-thunk').default;
const axios = require('axios');
const fetch = require('node-fetch');
const initialState = {
loading: false,
users: [],
error: ''
}
const FETCH_USERS_REQUEST = 'FETCH_USERS_REQUEST';
const FETCH_USERS_SUCCESS = 'FETCH_USERS_SUCCESS';
const FETCH_USERS_FAILURE = 'FETCH_USERS_FAILURE';
const fetchUsersRequest = () => {
return {
type : FETCH_USERS_REQUEST
}
};
const fetchUsersSuccess = users => {
return {
type : FETCH_USERS_SUCCESS,
payload : users
}
};
const fetchUsersFailure = error => {
return {
type : FETCH_USERS_FAILURE,
payload : error
}
};
const reducer = (state = initialState, action) => {
switch(action.type){
case FETCH_USERS_REQUEST:
return{
...state,
loading:true
};
case FETCH_USERS_SUCCESS:
return {
loading: false,
users: action.payload,
error : ''
}
case FETCH_USERS_FAILURE:
return {
loading: false,
users: [],
error: action.payload
};
}
};
// use jsonplaceholder
const fetchUsers = () => {
return function(dispatch) {
dispatch(fetchUsersRequest());
// url에 접속해서 Data를 얻는다.
axios.get('https://jsonplaceholder.typicode.cm/users')
.then(response => {
// 데이터
const users = response.data.map(user => user.id);
dispatch(fetchUsersSuccess(users));
})
.catch(err => {
// 에러
dispatch(fetchUsersFailure(err.message));
})
// url에 접속해서 Data를 얻는다.
// fetch('https://jsonplaceholder.typicode.com/users')
// .then(res => res.json())
// .then(response => {
// // 데이터
// console.log(response);
// const users = response.map(user => user.id);
// dispatch(fetchUsersSuccess(users));
// })
// .catch(err => {
// // 에러
// dispatch(fetchUsersFailure(err.message));
// })
}
}
const storage = createStore(reducer, applyMiddleware(thunkMiddleware));
storage.subscribe(() => { console.log(storage.getState())});
storage.dispatch(fetchUsers());
댓글
댓글 쓰기