
리덕스를 사용하는 어플리케이션에서 비동기 작업을 처리 할 때 사용하는 미들웨어이며, 이 미들웨어는 객체 대신 함수를 생성하는 액션 생성함수를 작성 할 수 있게 해준다.
일반 액션 생성자는 액션 객체를 생성하는 작업을 하는데, 특정 액션이 몇 초 뒤에(ex.서버에서 데이터를 불러온 후) 실행되게 하거나, 현재 상태에 따라 액션이 무시되게 하려면 일반 액션 생성자로는 실행할 수 없다.
특정 작업을 나중에 하도록 미루기 위해 함수 형태로 감싼것을 칭한다.
예를 들어, const x = 1 + 2;는 실행 즉시 return 되지만, const foo = () => 1 + 2; 함수 형태로 만들면 함수가 호출되어야 실행되는 원리다.
Redux Toolkit을 설치했다면 별도로 Redux-Thunk를 설치할 필요없다.
기존에는 App.js에서 비동기 작업을 하는 코드가 위치해 있었다.
// App.js
import styled from 'styled-components';
import MainHeader from './components/Header/MainHeader';
import Cart from './components/MyCart/Cart';
import Products from './components/ItemList/Products';
import { useDispatch, useSelector } from 'react-redux';
import { useEffect } from 'react';
import axios from 'axios';
import { showNotification } from './redux/slice/uiSlice';
import Notification from './components/UI/Notification';
const ItemArea = styled.div`
display: flex;
justify-content: center;
align-items: flex-start;
column-gap: 50px;
`;
// 렌더링 될 때 값이 변경되지 않도록 컴포넌트 외부에서 정의
let isInitial = true;
function App() {
const dispatch = useDispatch();
const showCart = useSelector(state => state.uiReducer.cartIsVisible);
const cart = useSelector(state => state.cartReducer);
const notification = useSelector(state => state.uiReducer.notification)
useEffect(() => {
const putCartItem = async () => {
// 전송중
dispatch(showNotification({
status: 'pending',
title: 'Sending...',
message: 'Sending cart data!'
}));
try {
await axios.put(`https://redux-http-97631-default-rtdb.asia-southeast1.firebasedatabase.app/cart.json`, cart)
dispatch(showNotification({
status: 'success',
title: 'Success!',
message: 'Sending cart data successfully!'
}));
} catch (err) {
dispatch(showNotification({
status: 'error',
title: 'Error!',
message: 'Sent cart data failed!'
}));
}
}
// 처음 렌더링 될 때 put 요청이 실행되므로 장바구니가 비어있는 상태로 오버라이딩 되는 것을 막기 위해
// putCartItem이 실행되기 전 return하여 차단
if (isInitial) {
isInitial = false;
return
}
putCartItem();
}, [cart, dispatch])
return (
<>
{notification && (
<Notification
status={notification.status}
title={notification.title}
message={notification.message}
/>
)}
<MainHeader />
<ItemArea>
<Products />
{showCart && <Cart />}
</ItemArea>
</>
);
}
export default App;
// cartSlice.js
export const sendCartData = (cart) => {
// 함수형태로 감싸줌
return async (dispatch) => {
dispatch(showNotification({
status: 'pending',
title: 'Sending...',
message: 'Sending cart data!'
}));
try {
await axios.put(`https://redux-http-97631-default-rtdb.asia-southeast1.firebasedatabase.app/cart.json`, cart)
dispatch(showNotification({
status: 'success',
title: 'Success!',
message: 'Sending cart data successfully!'
}));
} catch (err) {
dispatch(showNotification({
status: 'error',
title: 'Error!',
message: 'Sent cart data failed!'
}));
}
}
}
// App.js
import styled from 'styled-components';
import MainHeader from './components/Header/MainHeader';
import Cart from './components/MyCart/Cart';
import Products from './components/ItemList/Products';
import { useDispatch, useSelector } from 'react-redux';
import { useEffect } from 'react';
import Notification from './components/UI/Notification';
import { sendCartData } from './redux/slice/cartSlice';
const ItemArea = styled.div`
display: flex;
justify-content: center;
align-items: flex-start;
column-gap: 50px;
`;
// 렌더링 될 때 값이 변경되지 않도록 컴포넌트 외부에서 정의
let isInitial = true;
function App() {
const dispatch = useDispatch();
const showCart = useSelector(state => state.uiReducer.cartIsVisible);
const cart = useSelector(state => state.cartReducer);
const notification = useSelector(state => state.uiReducer.notification)
useEffect(() => {
// 처음 렌더링 될 때 put 요청이 실행되므로 장바구니가 비어있는 상태로 오버라이딩 되는 것을 막기 위해
// putCartItem이 실행되기 전 return하여 차단
if (isInitial) {
isInitial = false;
return;
}
dispatch(sendCartData(cart))
}, [cart, dispatch])
return (
<>
{notification && (
<Notification
status={notification.status}
title={notification.title}
message={notification.message}
/>
)}
<MainHeader />
<ItemArea>
<Products />
{showCart && <Cart />}
</ItemArea>
</>
);
}
export default App;
전체 코드