이제 까지 한 방법과 다른 방법은 작업 크리에이터를 사용하는 것이다.
이제 우리만의 작업 크리에이터를 만들 수 있고, 소위 썽크(thunk)를 만들 수 있다.
* thunk: 는 다른 함수가 완료될 때까지 기다리는 함수라고 볼 수 있다.
cart-slice.js 안에 이제까지 썻던 액션함수를 넣어서 쓸거임.
이전에 app.js에서 운용했던 모든 액션함수 fetch 관련 비동기 함수를 cart-slice.js에 모조리 넣을거임.
- 별도의 js함수에는 async/ await를 넣을 수 있기 때문에
- 오류 캐치는 try/ catch로..
- 함수로 만든 코드내 액션함수를 함수를 export하면서 이용하는 방식으로 사용할 것임.
import { createSlice } from '@reduxjs/toolkit';
import { uiActions } from './ui-slice';
const cartSlice = createSlice({
name: 'cart',
initialState: {
items: [],
totalQuantity: 0,
},
reducers: {
replaceCart(state, action) {
state.totalQuantity = action.payload.totalQuantity;
state.items = action.payload.items;
},
addItemToCart(state, action) {
const newItem = action.payload;
const existingItem = state.items.find((item) => item.id === newItem.id);
state.totalQuantity++;
if (!existingItem) {
state.items.push({
id: newItem.id,
price: newItem.price,
quantity: 1,
totalPrice: newItem.price,
name: newItem.title,
});
} else {
existingItem.quantity++;
existingItem.totalPrice = existingItem.totalPrice + newItem.price;
}
},
removeItemFromCart(state, action) {
const id = action.payload;
const existingItem = state.items.find((item) => item.id === id);
state.totalQuantity--;
if (existingItem.quantity === 1) {
state.items = state.items.filter((item) => item.id !== id);
} else {
existingItem.quantity--;
}
},
},
});
export const sendCartData = (cart) => {
return async (dispatch) => {
dispatch(
uiActions.showNotification({
status: 'pending',
title: 'Sending...',
message: 'Sending cart data!',
})
);
const sendRequest = async () => {
const response = await fetch(
'https://react-http-6b4a6.firebaseio.com/cart.json',
{
method: 'PUT',
body: JSON.stringify(cart),
}
);
if (!response.ok) {
throw new Error('Sending cart data failed.');
}
};
try {
await sendRequest();
dispatch(
uiActions.showNotification({
status: 'success',
title: 'Success!',
message: 'Sent cart data successfully!',
})
);
} catch (error) {
dispatch(
uiActions.showNotification({
status: 'error',
title: 'Error!',
message: 'Sending cart data failed!',
})
);
}
};
};
export const cartActions = cartSlice.actions;
export default cartSlice;
* 디스패치를 이용할 때 항상 작업 크리에이터 였지만 cart-slice에서는 다른 함수를 반환하는 함수를 대신 전달한다.
리덕스 툴킷에 의해 그러한 준비가 되어있다.
유형 프로퍼티가 있는 작업 개체만 허용하는 것이 아니라 함수를 반환하는 작업 크리에이터도 허용한다.
import { Fragment, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import Cart from './components/Cart/Cart';
import Layout from './components/Layout/Layout';
import Products from './components/Shop/Products';
import Notification from './components/UI/Notification';
import { sendCartData } from './store/cart-slice';
let isInitial = true;
function App() {
const dispatch = useDispatch();
const showCart = useSelector((state) => state.ui.cartIsVisible);
const cart = useSelector((state) => state.cart);
const notification = useSelector((state) => state.ui.notification);
useEffect(() => {
if (isInitial) {
isInitial = false;
return;
}
// 여기!!
dispatch(sendCartData(cart));
}, [cart, dispatch]);
return (
<Fragment>
{notification && (
<Notification
status={notification.status}
title={notification.title}
message={notification.message}
/>
)}
<Layout>
{showCart && <Cart />}
<Products />
</Layout>
</Fragment>
);
}
export default App;
* 이 컴포넌트는 이제 린 상태입니다. 여러 작업이 아닌 하나의 작업만 전달합니다. http 요청을 보내는 것을 신경 쓰지 않고 모든 힘든 작업은 리덕스 파일의 사용자 지정 작업 크리에이터 함수의 내부에서 발생함.
이와 같이 코드를 분할하는 것은 컴포넌트를 린 상대로 유지하기 때문에 좋은 것이라 할 수 있음.
* 데이터 가져오기
애플리케이션이 로드될 때 데이터를 가져오는 앱 작업 크리에이터를 빌드해보자
현재로서는 데이터만 보내고 있지만
애플리케이션이 로드될 때 데이터를 fetch 하지 않았기 때문에
다시 로드하면 모든 상태가 여전히 손실되어 있다. => 그렇게 되지 않게 데이터 firebase에서 가져올거임.
원리는 send하는거와 같습니다 다만 가져오고, 보낼때 http 요청이 다름으로 fetch할 때 해당 변수만 바꿔주면 되는것
import { uiActions } from './ui-slice';
import { cartActions } from './cart-slice';
export const fetchCartData = () => {
return async (dispatch) => {
const fetchData = async () => {
const response = await fetch(
'https://react-http-6b4a6.firebaseio.com/cart.json'
);
if (!response.ok) {
throw new Error('Could not fetch cart data!');
}
const data = await response.json();
return data;
};
try {
const cartData = await fetchData();
dispatch(
cartActions.replaceCart({
items: cartData.items || [],
totalQuantity: cartData.totalQuantity,
})
);
} catch (error) {
dispatch(
uiActions.showNotification({
status: 'error',
title: 'Error!',
message: 'Fetching cart data failed!',
})
);
}
};
};
* 위의 코드를 사용할 app.js
import { Fragment, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import Cart from './components/Cart/Cart';
import Layout from './components/Layout/Layout';
import Products from './components/Shop/Products';
import Notification from './components/UI/Notification';
import { sendCartData, fetchCartData } from './store/cart-actions';
let isInitial = true;
function App() {
const dispatch = useDispatch();
const showCart = useSelector((state) => state.ui.cartIsVisible);
const cart = useSelector((state) => state.cart);
const notification = useSelector((state) => state.ui.notification);
useEffect(() => {
dispatch(fetchCartData());
}, [dispatch]);
useEffect(() => {
if (isInitial) {
isInitial = false;
return;
}
if (cart.changed) {
dispatch(sendCartData(cart));
}
}, [cart, dispatch]);
return (
<Fragment>
{notification && (
<Notification
status={notification.status}
title={notification.title}
message={notification.message}
/>
)}
<Layout>
{showCart && <Cart />}
<Products />
</Layout>
</Fragment>
);
}
export default App;
* 지금까지 데이터를 가져오고 보내는 것을 리덕스를 이용해서 하는 법을 간단하게 흐름을 잡고 알아 봤다.
'React 실습' 카테고리의 다른 글
리액트를 베이스로 데이터를 요청하고 화면에 보여줘 보자! -(1) (0) | 2022.10.07 |
---|---|
useCallBack 훅 정리 (0) | 2022.09.03 |
리덕스 툴킷과 함께 백앤드 서버에 데이터 보내기, 데이터 가져오기(2) (0) | 2022.08.14 |
useRef대한 좋은 설명 (0) | 2022.08.14 |
리덕스 툴킷과 함께 백앤드 서버에 데이터 보내기, 데이터 가져오기 (0) | 2022.08.11 |