/cart
로 접속하면 장바구니 페이지를 보여주기
일단 <Route path="/cart" element{<Cart/>}
추가
//Cart.js 부트스트랩에서 가져옴
import {Table} from 'react-bootstrap'
<Table>
<thead>
<tr>
<th>#</th>
<th>상품명</th>
<th>수량</th>
<th>변경하기</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>안녕</td>
<td>안녕</td>
<td>안녕</td>
</tr>
</tbody>
</Table>
Redux는 props 없이 state를 공유할 수 있게 도와주는 라이브러리
js 파일 하나에 state를 보관하고 모든 컴포넌트가 직접 꺼내쓰기가 가능함.
리액트 취직시 redux 숙련도는 정말 중요!
npm install @reduxjs/toolkit react-redux
redux toolkit은 redux의 개선버전으로 문법이 더 쉽다.
설치하기 전 package.json 파일에 react와 react-dom 버전을 확인하고 ver18.1 이하라면 업데이트 해야 함.
이렇게 수정하고 파일 저장하고 터미널에서 npm install 실행하면 됨. 이거 하고 redux 설치하셈
import {configureStore} from '@reduxjs/toolkit'
export default configureStore({
reducer: { }
})
<Provider store={store}>로 <App/> 감싸기
결과: App과 모든 자식 컴포들은 자유롭게 state를 꺼내쓰기 가능
ccreateSlice({
name: "state이름",
initialState: "값",
}); //1. state 만들기 (useState 역할)
let user = createSlice({
//2. 변수에 슬라이스 할당
name: "user",
initialState: "kim",
});
let stock = createSlice({
//2. 변수에 슬라이스 할당
name: "stock",
initialState: [10, 11, 12],
});
export default configureStore({
reducer: {
user: user.reducer,
stock: stock.reducer,
},
});
(Cart.js)
import { useSelector } from "react-redux"
function Cart(){
let a = useSelector((state) => { return state } )
console.log(a)
return (생략)
}
console.log(a); // {user: 'kim', stock: Array(3)}
console.log(a.user); // kim
console.log(a.stock); //[10, 11, 12]
---------좀 더 편리하게 쓰기
let a = useSelector((state) => {
return state.user;
}); --> 편리하게 사용하기 : 저장소 안에 user라는 요소만 가져옴.
컴포넌트가 몇개 없거나 간단한 프로젝트면 props 쓰는게 낫다.
숙제 : 아래 데이터를 store에 보관하고 Cart.js 페이지에 가져와서 데이터 바인딩 하기. (힌트 : 데이터 개수에 맞게 표생성 반복문)
[
{id : 0, name : 'White and Black', count : 2},
{id : 2, name : 'Grey Yordan', count : 1}
]
store.js에 이렇게 slice 생성
let cart = createSlice({
name: "store",
initialState: [
{ id: 0, name: "White and Black", count: 2 },
{ id: 2, name: "Grey Yordan", count: 1 },
],
});
export default configureStore({
reducer: {
user: user.reducer,
stock: stock.reducer,
cart: cart.reducer,
},
});
Cart.js 로 돌아와서 map() 써서 state.cart 요소 개수만큼 생성해주면 됨.
{state.cart.map((item) => {
return (
<tr key={item.id}>
<td>{item.id}</td>
<td>{item.name}</td>
<td>{item.count}</td>
<td>안녕</td>
</tr>
);
})}
목표 : kim이라고 저장한거 John Kim으로 수정하기
let user = createSlice({
//2. 변수에 슬라이스 할당
name: "user",
initialState: "kim",
reducers: {
changeName(state) { // 함수 이름 맘대로.
return "john" + state;
},
},
});
slice 안에 reducers:{} 열고 안에 함수 생성
함수 작명 맘대로
파라미터 하나 작명하면 그건 기존 state가 됨.
return 우측에 새로운 state 입력하면 그게 기존 state를 갈아 치움.
결과 : changeName() 쓸 때마다 kim -> john kim 됨
export let {changeName} = user.actions
store.js 밑에 추가
slice이름.actions -> state 변경 함수가 전부 그 자리에 출력
그걸 변수에 저장해서 export하라는 뜻.
(Cart.js)
import { useDispatch, useSelector } from "react-redux"
import { changeName } from "./../store.js"
(생략)
const dispatch = useDispatch();
<button onClick={()=>{
dispatch(changeName())
}}>버튼임</button>
store안에 있는 state를 수정하기 위해
state를 수정하는 함수를 store.js에 만들어두고
컴포넌트는 그것을 호출만 해서 state 수정
편함.
프로젝트가 작다면 직접 state 변경하는게 편하지만
프로젝트가 커지면 state 변경하다 의도치 않은 값으로 바뀌는 버그가 발생하면 컴포넌트 100개면 100개 다 뒤져야 함.
redux 쓰면 store.js 만 잘 수정해주면 됨.
예습 )
Cart 페이지에 만들어둔 버튼 누르면
왼쪽에 있는 수량이 +1 되는 기능 만들기
단순히 index같은 것이나 store에서 해당하는 객체의 인덱스를 지정해서 값을 바꾸는 것은 상품이 많아지거나 순서가 바뀌는 등의 상황에서는 오류를 일으키는 원인이 된다. 그래서 해당하는 상품의 고유 id를 찾아 변경하는 식의 코드가 필요하다.
Cart 페이지에 장바구니 목록 만들어 두기
Redux2 에서 table을 이용해서 만들어 놨음.
Cart 변경하기 탭의 + 버튼 누르면 수량 ++
// store.js
let cart = createSlice({
name: "store",
initialState: [
{ id: 0, name: "White and Black", count: 2 },
{ id: 2, name: "Grey Yordan", count: 1 },
],
reducers: {
addCount(state, action) {
//1. find 메서드 사용(조건에 맞는 객체를 반환)
state.find((e) => e.id === action.payload).count++;
//2. findIndex 사용(조건에 맞는 객체의 index를 반환)
if (state.findIndex((e) => e.id === action.payload) !== -1) {
state[state.findIndex((e) => e.id === action.payload)].count++;
}
},
// Cart.js
<button onClick={()=>{
dispatch(addCount(state.cart[i].id));
// 해당하는 cart의 id를 넘겨줌.
}>
//store.js
,addList(state, action){
const index = state.findIndex((e)=>{ e.id === action.playload.id);
if(index > -1) {
state[index].count++;
//조건이 맞는 인덱스가 있으면 해당 객체의 카운트를 ++
}else{
state.push(action.payload)
// 없으면 객체를 인자로 받아서 push.
}
🧨 반성할 점
1. 문제) 주소창에 직접 path를 써서 이동하면 refresh 되는 것을 깜빡하고 store에 객체가 들어갔음이 확인이 되는데도 렌더링이 안되는 것에 대해 계속 헤매고 있었음.
해결) 메인페이지에 /path 경로로 Link하는 버튼을 만들고 Link하게 하니 리프레쉬되지 않고 추가된 객체가 잘 렌더링 돼있음을 확인했다.
2 문제) 객체의 프로퍼티를 조건으로 두고 해당하는 객체나 인덱스를 얻을 수 있는 메서드를 이용할 수 있음을 몰랐다. 검색하는 습관이 아직 덜 정착된 것 같으므로 습관을 잘 들여야겠다.
removeList(state, action) {
const index = state.findIndex((e) => e.id === action.payload.id);
console.log(index);
if (index > -1) {
state.splice(index, 1);
}
},
addList(state, action) {
// state.map((item) => {
// action.payload.id === item.id
// ? item.count++
// : console.log(action.payload);
// }); 맵 돌리면 어쩃든 false인 조건도 나오게 됨.
const index = state.findIndex((e) => e.id === action.payload.id);
if (index > -1) {
state[index].count++;
} else {
state.push(action.payload);
}
처음엔 map을 이용할 수 있을까 해서 map으로 해봤지만 이렇게 되면 무조건 삼항연산자의 false조건 반환 값이 나오게 된다. 따라서 틀린 접근.