react6
Redux Advanced
Async Action with Redux
- 비동기 작업을 어디서 하느냐가 제일 중요
- 액션을 분리합니다.
- Start
- Success
- Fail
- …등
- dispatch 를 할때 해줍니다.
- 당연히 리듀서는 동기적인 것 => Pure
- dispatch 도 동기적인 것
비동기 처리를 위한 액션 추가 (예시)
// 액션 정의
export const START_RECEIVE_BOOKS = "START_RECEIVE_BOOKS";
export const END_RECEIVE_BOOKS = "END_RECEIVE_BOOKS";
export const ERROR_RECEIVE_BOOKS = "ERROR_RECEIVE_BOOKS";
// 액션 생성자 함수
export function startReceiveBooks() {
return {
type: START_RECEIVE_BOOKS
};
}
export function endReceiveBooks(books) {
return {
type: END_RECEIVE_BOOKS,
books
};
}
export function errorReceiveBooks() {
return {
type: ERROR_RECEIVE_BOOKS
};
}
mapDispatchToProps => dispatch
const mapDispatchToProps = dispatch => ({
requestBooks: async token => {
dispatch(startLoading());
dispatch(clearError());
try {
const res = await axios.get("https://api.marktube.tv/v1/book", {
headers: {
Authorization: `Bearer ${token}`
}
});
dispatch(setBooks(res.data));
dispatch(endLoading());
} catch (error) {
console.log(error);
dispatch(setError(error));
dispatch(endLoading());
}
}
});
Books.jsx
import React, { useEffect } from "react";
const Book = props => <div>title : {props.title}</div>;
const Books = ({ token, loading, error, books, requestBooks }) => {
useEffect(() => {
requestBooks(token); // 컨테이너로 로직을 옮겼음.
}, [token, requestBooks]);
return (
<div>
{loading && <p>loading...</p>}
{error !== null && <p>{error.message}</p>}
{books.map(book => (
<Book title={book.title} key={book.bookId} />
))}
</div>
);
};
export default Books;
리덕스 미들웨어
- 미들웨어가 “디스패치” 의 앞뒤에 코드를 추가할수 있게 해줍니다.
- 미들웨어가 여러개면 미들웨어가 “순차적으로” 실행됩니다.
- 두 단계가 있습니다.
- 스토어를 만들때, 미들웨어를 설정하는 부분
- {createStore, applyMiddleware} from redux
- 디스패치가 호출될때 실제로 미들웨어를 통과하는 부분
- 스토어를 만들때, 미들웨어를 설정하는 부분
- dispatch 메소드를 통해 store로 가고 있는 액션을 가로채는 코드
function middleware1(store) {
return next => {
console.log("middleware1", 1);
return action => {
console.log("middleware1", 2);
const returnValue = next(action);
console.log("middleware1", 3);
return returnValue;
};
};
}
function middleware2(store) {
return next => {
console.log("middleware2", 1);
return action => {
console.log("middleware2", 2);
const returnValue = next(action);
console.log("middleware2", 3);
return returnValue;
};
};
}
applyMiddleware(함수1, 함수2, …)
import { createStore, applyMiddleware } from 'redux';
function middleware1(store) {...}
function middleware2(store) {...}
const store = createStore(reducer, applyMiddleware(middleware1, middleware2));
middleware 에서 store 접근
function middleware1(store) {
return next => {
console.log("middleware1", 1, store.getState());
return action => {
console.log("middleware1", 2, store.getState());
const returnValue = next(action);
console.log("middleware1", 3, store.getState());
return returnValue;
};
};
}
react-devtools
npm install -D redux-devtools-extension
composeWithDevTools
import { createStore, applyMiddleware } from "redux";
import reducers from "./reducers";
import { composeWithDevTools } from "redux-devtools-extension";
const store = createStore(reducers, composeWithDevTools(applyMiddleware()));
export default store;
redux-thunk
- 리덕스 미들웨어
- 리덕스를 만든 사람이 만들었음. (Dan)
- 리덕스에서 비동기 처리를 위한 라이브러리
- 액션 생성자를 활용하여 비동기 처리
- 액션 생성자가 액션을 리턴하지 않고, 함수를 리턴함.
npm i redux-thunk
import thunk from ‘redux-thunk’;
import { createStore, applyMiddleware } from "redux";
import reducers from "./reducers";
import { composeWithDevTools } from "redux-devtools-extension";
import thunk from "redux-thunk"; // import
const store = createStore(
reducers,
composeWithDevTools(applyMiddleware(thunk)) // 미들웨어 설정
);
export default store;
Before Using thunk
const mapDispatchToProps = dispatch => ({
requestBooks: async token => {
dispatch(startLoading());
dispatch(clearError());
try {
const res = await axios.get("https://api.marktube.tv/v1/book", {
headers: {
Authorization: `Bearer ${token}`
}
});
dispatch(setBooks(res.data));
dispatch(endLoading());
} catch (error) {
console.log(error);
dispatch(setError(error));
dispatch(endLoading());
}
}
});
Use thunk
// BooksContainer.jsx
const mapDispatchToProps = dispatch => ({
requestBooks: async token => {...},
requestBooksThunk: token => {
dispatch(setBooksThunk(token));
}
});
// actions/index.js
export const setBooksThunk = token => async dispatch => {
dispatch(startLoading());
dispatch(clearError());
try {
const res = await axios.get("https://api.marktube.tv/v1/book", {
headers: {
Authorization: `Bearer ${token}`
}
});
dispatch(setBooks(res.data));
dispatch(endLoading());
} catch (error) {
console.log(error);
dispatch(setError(error));
dispatch(endLoading());
}
};
redux-promise-middleware
npm i redux-promise-middleware
import promise from ‘redux-promise-middleware’;
import { createStore, applyMiddleware } from "redux";
import reducers from "./reducers";
import { composeWithDevTools } from "redux-devtools-extension";
import thunk from "redux-thunk";
import promise from "redux-promise-middleware"; // import
const store = createStore(
reducers,
composeWithDevTools(applyMiddleware(thunk, promise)) // 미들웨어 설정
);
export default store;
payload 가 Promise
// actions/index.js
export const setBooksPromise = token => ({
type: BOOKS,
payload: axios.get("https://api.marktube.tv/v1/book", {
headers: {
Authorization: `Bearer ${token}`
}
})
});
액션의 type 에 접미사를 붙인 액션을 자동 생성하고 자동으로 dispatch 시킴
// actions/index.js
export const BOOKS = "BOOKS";
export const BOOKS_PENDING = "BOOKS_PENDING";
export const BOOKS_FULFILLED = "BOOKS_FULFILLED";
export const BOOKS_REJECTED = "BOOKS_REJECTED";
// reducers/loading.js
export default function loading(state = initialState, action) {
switch (action.type) {
case BOOKS_PENDING:
return true;
case BOOKS_FULFILLED:
return false;
case BOOKS_REJECTED:
return false;
default:
return state;
}
}
payload 로 들어오는 데이터를 활용하여 표현
// reducers/books.js
const books = (state = initialState, action) => {
switch (action.type) {
case BOOKS_FULFILLED: {
return [...action.payload.data]
}
...
}