2022.07.06(Wed)
[TIL] Day51
[SEB FE] Day50
โย Redux
๋ React
์์ด๋ ์ฌ์ฉํ ์ ์๋ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
Action
๊ฐ์ฒด ์์ฑAction
๊ฐ์ฒด๊ฐ Dispatch
ํจ์ ์ธ์๋ก ์ ๋ฌDispatch
ํจ์๊ฐ Action
๊ฐ์ฒด๋ฅผ Reducer
ํจ์๋ก ์ ๋ฌStore
์ํ ๋ณ๊ฒฝAction
๐ย Dispatch
๐ย Reducer
๐ย Store
๐ซ ย Redux
์์ ์์ ๊ฐ์ ์์๋ก ๋ฐ์ดํฐ๊ฐ ๋จ๋ฐฉํฅ์ผ๋ก ํ๋ฆ
์ด๋ค ์ก์ ์ ์ทจํ ๊ฒ์ธ์ง ์ ์ํด ๋์ ๊ฐ์ฒด
// payload ํ์X
{type: "INCREASE'} // type์ ๋๋ฌธ์ & Snake_Case๋ก ์์ฑํ๊ธฐ!
// payload ํ์O
{type: "SET_NUMBER', payload: 10} // payload๋ก ๊ตฌ์ฒด์ ์ธ ๊ฐ ์ ๋ฌ
โย type
(ํด๋น Action ๊ฐ์ฒด๊ฐ ์ด๋ค ๋์์ ํ๋์ง ๋ช
์) ํ์ ์ง์ !
โย Action ๊ฐ์ฒด ์์ฑ ํจ์๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ โ ์ก์
์์ฑ์(Action Creator
)
// [Action Creator]
// payload ํ์X
const increase = () => {
return {
type: 'INCREASE'
}
}
// payload ํ์O
const setNumber = (num) => {
return {
type: 'SET_NUMBER',
payload: num
}
}
Reducer
๋กAction
์ ์ ๋ฌํด์ฃผ๋ ํจ์
// Action ๊ฐ์ฒด ์ง์ ์์ฑํ๋ ๊ฒฝ์ฐ
dispatch({type: 'INCREASE'});
dispatch({type: 'SET_NUMBER', payload: 10});
// Action Creator ์ฌ์ฉํ๋ ๊ฒฝ์ฐ
dispatch(increase());
dispatch(setNumber(10));
// => Action ๊ฐ์ฒด๋ฅผ ์ ๋ฌ๋ฐ์ Dispatchํจ์๋ Reducer ํธ์ถ
Dispatch
์๊ฒ์ ์ ๋ฌ๋ฐ์Action
๊ฐ์ฒด์type
๊ฐ์ ๋ฐ๋ผ์ ์ํ๋ฅผ ๋ณ๊ฒฝ์ํค๋ ํจ์
const count =1;
const counterReducer = (state = count, action) { // ์ธ์๋ก ์ด๊ธฐ์ํ ์ค์
switch(action.type). // Action๊ฐ์ฒด์ type๊ฐ์ ๋ฐ๋ฅธ ์กฐ๊ฑด๋ฌธ
case 'INCREASE': // action === 'INCREASE'
return state + 1;
case 'DECREASE': // action === 'DECREASE'
return state - 1;
case 'SET_NUMBER': // action === 'SET_NUMBER'
return action.payload;
default: // ์์ ์กฐ๊ฑด์ ๋ชจ๋ ํด๋น๋์ง ์์ ๋ ๊ธฐ์กด ์ํ return
return state;
} // Reducer๊ฐ ๋ฆฌํดํ๋ ๊ฐ์ด ์๋ก์ด ์ํ!
โย Reducer
๊ฐ ์์ํจ์์ฌ์ผ ์ธ๋ถ ์์ธ์ผ๋ก ์ธํด ์๋ฑํ ๊ฐ์ผ๋ก ์ํ๊ฐ ๋ณ๊ฒฝ๋๋ ์ผ์ด ์์ด์ง
// ์ฌ๋ฌ ๊ฐ์ Reducer๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ
// combineReduers() ๋ฉ์๋๋ฅผ ์ด์ฉํด์ ํ๋์ Reducer๋ก ํฉ์ ธ์ค ์ ์์
import {combineReduers} from 'redux';
const rootReducer = combineReducers({
counterReducer,
anyReduer,
...
});
์ํ๊ฐ ๊ด๋ฆฌ๋๋ ํ๋๋ฟ์ธ ์ ์ฅ์ ์ญํ
์ฆ, state๊ฐ ์ ์ฅ๋์ด ์๋ ๊ณต๊ฐ
import {createStore} from 'redux';
const store = createStore(rootReducer);
useSelector()
: ์ปดํฌ๋ํธ & state๋ฅผ ์ฐ๊ฒฐํ์ฌ Redux์ state์ ์ ๊ทผํ ์ ์๊ฒ ํด์ฃผ๋ ๋ฉ์๋
import {useSelector} from 'react-redux'; // 'redux'์์ ๋ถ๋ฌ์ค๋๊ฒ ์๋๋ผ๋ ๊ฒ์ ์ฃผ์!
const counter = useSelector(state => state.counterReducer)
console.log(counter); // 1
useDispatch()
: Action ๊ฐ์ฒด๋ฅผ Reducer๋ก ์ ๋ฌํด์ฃผ๋ ๋ฉ์๋
import {useDispatch} from 'react-redux';
const dispatch = useDispatch();
dispatch(increase());
console.log(counter) // 2
dispatch(setNumber(10));
console.log(counter) // 10
Single source of truth
Store
์กด์ฌState is read-only
Changes are mad with pure functions
์ค๋์ ์ด์ ๊ตฌํํ๋ Cmarket Hooks ๊ธฐ๋ฅ๋ค์ Redux๋ฅผ ํ์ฉํ์ฌ Refactoringํด๋ณด๋ ์๊ฐ์ ๊ฐ์ก๋ค.
// action types
export const ADD_TO_CART = "ADD_TO_CART";
export const REMOVE_FROM_CART = "REMOVE_FROM_CART";
export const SET_QUANTITY = "SET_QUANTITY";
export const NOTIFY = "NOTIFY";
export const ENQUEUE_NOTIFICATION = "ENQUEUE_NOTIFICATION";
export const DEQUEUE_NOTIFICATION = "DEQUEUE_NOTIFICATION";
// actions creator functions
// ์ฅ๋ฐ๊ตฌ๋ ๋ด๊ธฐ ACTION
export const addToCart = (itemId) => {
return {
type: ADD_TO_CART,
payload: { // payload๋ก ๊ตฌ์ฒด์ ์ธ ๊ฐ์ ์ค์ ํด์ฃผ๊ธฐ
quantity: 1, // ์ฅ๋ฐ๊ตฌ๋์ ๋ด์ ๋ ์๋์ด 1๋ก ๊ณ ์ ๋์ด ์์ผ๋ฏ๋ก ๊ฐ์ 1๋ก ์ค์
itemId,
},
};
};
// ์ฅ๋ฐ๊ตฌ๋ ์ญ์ ACTION
export const removeFromCart = (itemId) => {
return {
type: REMOVE_FROM_CART,
payload: {
itemId, // ์ญ์ action์์๋ quantity(์๋) ๊ฐ์ ๊ตณ์ด ํ์์์ผ๋ฏ๋ก id๋ง ๊ฐ ์ค์ !
},
};
};
// ์๋๋ณ๊ฒฝ ACTION
export const setQuantity = (itemId, quantity) => {
return {
type: SET_QUANTITY,
payload: {
itemId,
quantity,
},
};
};
// ...
์ฅ๋ฐ๊ตฌ๋์ ์ถ๊ฐ
const handleClick = (item) => {
// ์ฅ๋ฐ๊ตฌ๋์ ํด๋ฆญํ ํด๋น ํ๋ชฉ์ด ์กด์ฌํ์ง ์๋๋ค๋ฉด
if (!cartItems.map((el) => el.itemId).includes(item.id)) {
dispatch(notify(`์ฅ๋ฐ๊ตฌ๋์ ${item.name}์ด(๊ฐ) ์ถ๊ฐ๋์์ต๋๋ค.`));
dispatch(addToCart(item.id)); // dispatch()๋ก ์์ดํ
์ถ๊ฐ์ ๋ํ ์ก์
์ ๋ฌ
} else { // ํด๋ฆญํ ํ๋ชฉ์ด ์ด๋ฏธ ์ฅ๋ฐ๊ตฌ๋์ ์๋ ํ๋ชฉ์ด๋ผ๋ฉด
dispatch(notify("์ด๋ฏธ ์ถ๊ฐ๋ ์ํ์
๋๋ค."));
}
};
์ฅ๋ฐ๊ตฌ๋์์ ํ๋ชฉ ์ญ์
const handleQuantityChange = (quantity, itemId) => {
dispatch(setQuantity(itemId, quantity)); // ์ฅ๋ฐ๊ตฌ๋ ์์ดํ
์๋๋ณ๊ฒฝ ์ก์
์ ๋ฌ
};
์ฅ๋ฐ๊ตฌ๋์์ ํ๋ชฉ ์๋ ๋ณ๊ฒฝ
const handleDelete = (itemId) => {
setCheckedItems(checkedItems.filter((el) => el !== itemId));
dispatch(removeFromCart(itemId)); // ์ฅ๋ฐ๊ตฌ๋ ์์ดํ
์ญ์ ์ก์
์ ๋ฌ
};
import { REMOVE_FROM_CART, ADD_TO_CART, SET_QUANTITY } from "../actions/index";
import { initialState } from "./initialState"; // dummy data
const itemReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TO_CART:
return Object.assign({}, state, { // Object.assign()์ ํตํด ์๋ก์ด ๊ฐ์ฒด ์์ฑ
// ๊ธฐ์กด ๋ฐ์ดํฐ์ ์๋ก์ด ๋ฐ์ดํฐ ์ฆ, action.payload๋ฅผ ๋ํด์ค
cartItems: [...state.cartItems, action.payload],
});
case REMOVE_FROM_CART:
// ์ฅ๋ฐ๊ตฌ๋ ์์ดํ
id์ action.payload์ id๋ฅผ ๋น๊ตํด์ ๊ฑฐ๋ฅธ ์์ดํ
let deletedItem = state.cartItems.filter(
(el) => el.itemId !== action.payload.itemId
);
return Object.assign({}, state, {
cartItems: deletedItem, // ๊ฑฐ๋ฅธ ์ญ์ ํ ์์ดํ
์ cartItems๋ก.
});
case SET_QUANTITY:
// ์ฅ๋ฐ๊ตฌ๋ ์์ดํ
id์ action.payload์ id๊ฐ ๊ฐ์ ๋์ ํด๋น ์์ดํ
์ index
let idx = state.cartItems.findIndex(
(el) => el.itemId === action.payload.itemId
);
return Object.assign({}, state, {
// ์์ ํ ์์ดํ
์ , ํ๋ ๊ธฐ์กด ๋ฐ์ดํฐ๋ก ์ ์งํ๋,
// ์์ ํ ์์ดํ
์ธ action.payload๋ฅผ ๊ทธ ์ค๊ฐ์ ๋ผ์์ ๋ํด์ค
// ์ด ๋ธ๋ก๊ทธ์์ ๋ฐฉ๋ฒ ์์ด๋์ด ์ป์! -> https://florescene.tistory.com/m/199
cartItems: [
...state.cartItems.slice(0, idx), //
action.payload,
...state.cartItems.slice(idx + 1),
],
});
default:
return state;
}
};
export default itemReducer;
๐ซ Reducer์ ๋ถ๋ณ์ฑ(Immutability)
: Redux์ state ์
๋ฐ์ดํธ๋ immutableํ ๋ฐฉ์์ผ๋ก ๋ณ๊ฒฝํด์ผ ํจ
๐คทโโ๏ธย why? Redux ์ฅ์ ์ค ํ๋์ธ ๋ณ๊ฒฝ๋ state๋ฅผ ๋ก๊ทธ๋ก ๋จ๊ธฐ๊ธฐ ์ํด
๐ย Object.assign()
์ ํตํด ์๋ก์ด ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ๋ฆฌํด! (spread ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๊ณผ ์ ์ฌํ ๊ธฐ๋ฅ)
โฐ Test 12/12 Pass โ๏ธ
์ด์ ๋ ๊ทธ๋ ๊ณ , ์ค๋๋ ์ ๋ ์ ์ธ์์ ํด์ ์ ๋ ์๊ฐ์ ๊ณต๋ถ๋์ด ํ์ ํ ์ค์ด๋ค์๋ค.๐ฅฒ
์ด์ ์์ ๋ชปํ๊ตฌ, ์ค๋๋ ์ง์ค๋๊น ์กธ๋ฆฌ๊ณ ํผ๊ณคํด์ ์ง์ค์ด ์๋๋ ๋๋์ฐ..
๋ด์ผ ํ๋ฃจ๋ ํ์ง๋ถ์ง ๋ณด๋ด์ง ๋ง๊ธฐ ๐
๊ทธ๋ฆฌ๊ณ ๊ณ์ ๊ณ ๋ฏผํ๊ณ ๊ณ ๋ฏผํ๋๋ชจ๋์์ค ๋ฅ๋ค์ด๋ธ
๋์๋ฅผ ebook์ผ๋ก ๊ตฌ๋งคํ๋ค. 10์๊น์ง ๋ถ์ง๋ฐํ ์ฝ์ด๋ณด์ ๐ฆพ