[React] 쇼핑몰 만들기(part2)-3

JH Cho·2022년 9월 8일
1

코딩애플

목록 보기
2/4

Redux

redux(1)

장바구니 페이지 만들기(Redux Toolkit 설치)

/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의 장점

Redux는 props 없이 state를 공유할 수 있게 도와주는 라이브러리


js 파일 하나에 state를 보관하고 모든 컴포넌트가 직접 꺼내쓰기가 가능함.

리액트 취직시 redux 숙련도는 정말 중요!

Redux 설치하기

npm install @reduxjs/toolkit react-redux
redux toolkit은 redux의 개선버전으로 문법이 더 쉽다.

설치하기 전 package.json 파일에 react와 react-dom 버전을 확인하고 ver18.1 이하라면 업데이트 해야 함.

이렇게 수정하고 파일 저장하고 터미널에서 npm install 실행하면 됨. 이거 하고 redux 설치하셈

Redux 세팅

  1. 아무데나 store.js 파일 생성 아래 코드 넣기
    (store.js는 state를 보관하는 저장소)
import {configureStore} from '@reduxjs/toolkit'

export default configureStore({
  reducer: { }
}) 
  1. index.js 파일가서 Provider 컴포와 아까 작성한 파일(store.js) import.
  2. <Provider store={store}>로 <App/> 감싸기

결과: App과 모든 자식 컴포들은 자유롭게 state를 꺼내쓰기 가능

Redux-2

store에 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,
  },
});

Redux store에 있는 state 가져오기

(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>
            );
          })}

Redux(3)

store의 state 변경하기

  1. store.js에 state 변경 함수 생성
  2. export
  3. 필요할 때 import해서 쓰면 되는데 dispatch()로 감싸서 사용해야 함.

1. store.js 안에 state 수정해주는 함수 생성

목표 : 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 됨

    2. 다른 곳에서 쓰기 좋게 export 해둠

    export let {changeName} = user.actions
    store.js 밑에 추가
    slice이름.actions -> state 변경 함수가 전부 그 자리에 출력
    그걸 변수에 저장해서 export하라는 뜻.

3. 원할 때 import해서 사용. (dispatch()로 감쌀 것)

(Cart.js)

import { useDispatch, useSelector } from "react-redux"
import { changeName } from "./../store.js"

(생략) 
const dispatch = useDispatch();
<button onClick={()=>{
  dispatch(changeName())
}}>버튼임</button> 
  • storedㅔ서 원하는 state 변경 함수 가져오고
  • useDispatch라는 것도 라이브러리에서 가져오고
  • dispatch(state변경함수()) <- 이렇게 실행

어렵네

store안에 있는 state를 수정하기 위해
state를 수정하는 함수를 store.js에 만들어두고
컴포넌트는 그것을 호출만 해서 state 수정

왜씀?

편함.

프로젝트가 작다면 직접 state 변경하는게 편하지만

프로젝트가 커지면 state 변경하다 의도치 않은 값으로 바뀌는 버그가 발생하면 컴포넌트 100개면 100개 다 뒤져야 함.


redux 쓰면 store.js 만 잘 수정해주면 됨.

예습 )
Cart 페이지에 만들어둔 버튼 누르면
왼쪽에 있는 수량이 +1 되는 기능 만들기

Redux(4)

장바구니 기능 만들기

단순히 index같은 것이나 store에서 해당하는 객체의 인덱스를 지정해서 값을 바꾸는 것은 상품이 많아지거나 순서가 바뀌는 등의 상황에서는 오류를 일으키는 원인이 된다. 그래서 해당하는 상품의 고유 id를 찾아 변경하는 식의 코드가 필요하다.

  1. Cart 페이지에 장바구니 목록 만들어 두기
    Redux2 에서 table을 이용해서 만들어 놨음.

  2. 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를 넘겨줌.
}>
  1. detail 페이지에서 주문하기 버튼 누르면 해당 상품 장바구니에 추가
//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 문제) 객체의 프로퍼티를 조건으로 두고 해당하는 객체나 인덱스를 얻을 수 있는 메서드를 이용할 수 있음을 몰랐다. 검색하는 습관이 아직 덜 정착된 것 같으므로 습관을 잘 들여야겠다.

Redux5

응용해보기

  1. 장바구니 삭제 기능
    reducer 만들어서 받은 인자와 일치함을 조건으로 두고 해당 요소를 splice로 제거하는 방식으로 만들었음.
removeList(state, action) {
      const index = state.findIndex((e) => e.id === action.payload.id);
      console.log(index);
      if (index > -1) {
        state.splice(index, 1);
      }
    },
  1. 상품 주문하기 시 장바구니에 중복 리스트로 올라가지 않고 count되게 하기.
    같은 id가 중복되는가를 조건으로 두고 count를 올리는 경우와 배열에 요소를 추가하는 코드를 각각 부여했음.
    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조건 반환 값이 나오게 된다. 따라서 틀린 접근.

참고 - https://darrengwon.tistory.com/1406

profile
주먹구구식은 버리고 Why & How를 고민하며 프로그래밍 하는 개발자가 되자!

0개의 댓글