import { combineReducers } from 'redux';
import itemReducer from './itemReducer';
import notificationReducer from './notificationReducer';
const rootReducer = combineReducers({
itemReducer,
notificationReducer //2개의 리듀서를 합쳐줌
});
export default rootReducer;
일부는 생략되었습니다
//src/reducers/notificationReducer.js
const notificationReducer = (state = {notifications:[]}, action) => {
switch (action.type) {
case ENQUEUE_NOTIFICATION:
return Object.assign({}, state, {
//notifications: ...state.notifications, action.payload
})
case DEQUEUE_NOTIFICATION:
return Object.assign({}, state, {
//notifications:state.notifications.slice(1)
})
//state.notification에서 첫번째 요소를 뺀 나머지 배열을 notification으로...payload 초기화용?
default:
return state;
}
}
export default notificationReducer;
//index.js
//생략
.
.
.
export const notify = (message, dismissTime = 5000) => dispatch => {
const uuid = Math.random()
dispatch(enqueueNotification(message, dismissTime, uuid))
setTimeout(() => {
dispatch(dequeueNotification())
}, dismissTime)
}
export const enqueueNotification = (message, dismissTime, uuid) => { //Payload가 필요하다.
return {
type: ENQUEUE_NOTIFICATION,
payload: {
message,
dismissTime,
uuid
}
}
}
export const dequeueNotification = () => {
return {
type: DEQUEUE_NOTIFICATION
}
}
import { useSelector } from 'react-redux';
import Toast from './Toast';
function NofiticationCenter() {
const state = useSelector(state => state.notificationReducer);
//state는 notificationReducer 선택
return <div className="notification-container top-right">
{
state.notifications.map((n) =>
<Toast key={n.uuid} text={n.message} dismissTime={n.dismissTime} />
//Toast에는 time에따라 fadeout하는 함수 포함 + text 출력
)
}
</div>
} //
export default NofiticationCenter
왜 굳이 dequeueNotification 함수를 작성했는지 모르겠다...이미 notification된 state를 삭제하기 위해서 인듯 한데...세션 때 다시 복습해야겠다.
일부는 생략되었습니다
index.js (/actions)
export const addToCart = (itemId) => {
return {
type: ADD_TO_CART,
payload: {
quantity: 1,
itemId
}
}
}
export const removeFromCart = (itemId) => {
return {
type: REMOVE_FROM_CART,
payload : {itemId}
}
}
export const setQuantity = (itemId, quantity) => {
return {
type : SET_QUANTITY,
payload : {quantity, itemId}
}
}
새로 카트에 담는 addToCart 는 quantity를 1로 지정
//itemListContainer.js
function ItemListContainer() {
const state = useSelector(state => state.itemReducer);
const { items, cartItems } = state;
const dispatch = useDispatch();
//생성한 action을 useDispatch를 통해 발생시킬 수 있다
const handleClick = (item) => {
if (!cartItems.map((el) => el.itemId).includes(item.id)) {
//카트에 있는 아이템이 아닐경우
dispatch(addToCart(item.id))
//item 담기
dispatch(notify(`장바구니에 ${item.name}이(가) 추가되었습니다.`))
}
else {
dispatch(notify('이미 추가된 상품입니다.'))
}
}
.
.
(생략)
//itemReducer.js
import { REMOVE_FROM_CART, ADD_TO_CART, SET_QUANTITY } from "../actions/index";
import { initialState } from "./initialState";
//initialState에는 cartitems과 items 객체 정보가 들어있다.
const itemReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TO_CART:
return Object.assign({}, state, {
cartItems: [...state.cartItems, action.payload]
//cartItems에 새 객체(action.payload) 추가.
})
break;
case REMOVE_FROM_CART:
let currentItem = state.cartItems.filter((el) => el.itemId !== action.payload.itemId)
//필터 메소드를 활용해 payload itemId와 같지 않은 것만 currentItem으로 설정
return Object.assign({}, state, {
cartItems: currentItem
// cartItems를 currentItem으로 설정
})
break;
case SET_QUANTITY:
let idx = state.cartItems.findIndex(el => el.itemId === action.payload.itemId)
return {
...state,
cartItems: [...state.cartItems.slice(0, idx), action.payload, ...state.cartItems.slice(idx + 1)]
}
break;
default:
return state;
}
}
export default itemReducer;
Object.assign()
메소드를 활용하여 기존 객체에 새 정보를 덮어씌울 수 있다. case ADD_TO_CART에서 객체를 리턴하게 되는데, 이 객체는 기존 state.cartItems에 action.payload ({quantity: 1,itemId})
를 추가하여 리턴한다. case REMOVE_FROM_CART에서는 필터 메소드를 통해 payload itemId와 같지 않은 것만 currentItem으로 설정한다. 그 이후 Object.assign()
메소드를 활용하여 cartItems를 currentItem으로 설정하면 된다. (currentItem은 이미 배열이다.) case SET_QUANTITY는 action.payload와 같은 인덱스를 같는 cartItem을 찾아서 , cartItems에서 그 인덱스에 해당하는 요소를 제거한 후, quantity 값이 바뀐 action.payload(즉 새로운 값) 을 삽입해준다.
ShoppingCart.js
export default function ShoppingCart() {
const state = useSelector(state => state.itemReducer);
const { cartItems, items } = state
const dispatch = useDispatch();
const [checkedItems, setCheckedItems] = useState(cartItems.map((el) => el.itemId))
(생략)
.
.
const handleQuantityChange = (quantity, itemId) => {
dispatch(setQuantity(itemId,quantity))
}
const handleDelete = (itemId) => {
setCheckedItems(checkedItems.filter((el) => el !== itemId))
dispatch(removeFromCart(itemId));
}
dispatch 함수를 호출하여 액션을 전달하였다.