본문 바로가기
웹/24-StudyWithPnP

[React+JS/리액트] 웹 스터디 9주차

by 보먀 2024. 7. 4.
728x90
반응형

Redux - 4

 

redux state 가 array/object 인 경우 변경하기

 

{name : 'kim', age : 20} 이렇게 생긴 객체의 'kim' 을 'park' 로 변경하고 싶을 때 두 가지 변경 방법이 존재한다.

 

1번 - 변경함수를 통해 return 오른쪽에 적은 데이터로 기존 state 를 갈아치워주는 방법

2번 - state 를 직접 수정하는 방법

 

2 번처럼 state 를 직접 수정하는 문법 사용이 가능한 이유는 Immer.js 라이브러리가 state 사본을 하나 더 생성해주기 때문이다. ( (Immer.js 라이브러리는 Redux Tookit 에 내장되어 있어 Redux 설치시 자동으로 딸려온다)

// 1
let user = createSlice({
  name : 'user',
  initialState : {name : 'kim', age : 20},
  reducers : {
    changeName(state){
      return {name : 'park', age : 20}
    }
  }
})

// 2
let user = createSlice({
  name : 'user',
  initialState : {name : 'kim', age : 20},
  reducers : {
    changeName(state){
      state.name = 'park'
    }
  }
})

 

 

두가지 방법이 존재하지만 2번 방법을 추천한다. array/object 자료의 경우 state 를 직접 수정이 훨씬 편하기 때문이다. 

참고로 state 만들 때 문자나 숫자 하나만 필요해도 redux 에선 일부러 object 나 array 담는 경우가 있는데, 이렇게 했을 때 수정하기가 훨씬 편하기 때문이다. 

 

 

- 사이트 아무데나 버튼 만들고, 버튼을 누르면 user 의 age 항목이 +1 되도록 만들기

// userSlice.js

import { createSlice } from "@reduxjs/toolkit"

let user = createSlice({ 
    name: 'user',
    initialState: { name: 'kim', age: 20 },
    reducers : {
        changeName(state) {
            state.name = 'park'
        },
        clickButton(state) {
            state.age += 1
        }
    },
})

export let { changeName, clickButton } = user.actions

export default user
// Cart.js
<button onClick={()=>{
	dispatch(clickButton())}}>나이증가</button>

 

 

만약 버튼을 눌렀을 때 +1 이 아니라 임의의 수만큼을 증가시키고 싶다면, 함수의 파라미터 이용하기

 

함수의 파라미터는 관습적으로 action 이라고 작명을 한다. 

action.type 를 하면 state 변경함수 이름이 나오고, action.payload 하면 파라미터가 나온다. 

 

payload 는 화물, 소포, 택배 등의 뜻인데 파라미터가 메세지에 실어보내는 화물이라 생각하면 된다. 

 

파라미터를 사용할 때는 뒤에 꼭 payload 를 써줘야 하는데, action 에는 화물 말고도 여러 정보들이 들어가 있기 때문이다. 

// userSlice.js

import { createSlice } from "@reduxjs/toolkit"

let user = createSlice({ 
    name: 'user',
    initialState: { name: 'kim', age: 20 },
    reducers : {
        changeName(state) {
            state.name = 'park'
        },
        clickButton(state, action) {
            state.age += action.payload
        }
    },
})

export let { changeName, clickButton } = user.actions

export default user
// Cart.js
<button onClick={()=>{ 
	dispatch(clickButton(100))}}>나이증가</button>

 

 

 

숙제

1. 장바구니 페이지에서 품목의 + 버튼을 누르면 해당 상품의 수량부분이 +1 되는 기능 만들기

2. 상세페이지 주문하기 버튼을 누르면 새로운 상품이 state 에 추가되는 기능 만들기

 

1번 기능을 구현할 때

0 번째 버튼을 누르면 0 번째 상품 +1, 1 번째 버튼을 누르면 1 번째 상품 +1 

이런 식으로 구현하게 되면 장바구니 상품 정렬의 순서가 바뀌거나 했을 때 버그가 일어날 가능성이 있다.

 

그래서 버튼을 눌렀을 때 상품의 고유 id 와 동일한 상품 id 를 가진 state 를 찾아서 개수를 +1 시켜줘야 한다.

아래 코드에서는 findIndex 라는 함수를 써서 동일한 상품 id 를 가진 상품을 찾아서 개수를 +1 해주는 방식으로 구현하였다. 

 

2번은 간단하게 구현할 수 있었는데, 상세페이지에서 주문하기 버튼을 누르면 그 상품의 정보들을 객체 담고 state 변경함수에서 push 함수를 통해 기존 state 에 새로운 객체를 넣어주는 방식으로 구현하였다. 

// store.js

import { configureStore, createSlice } from '@reduxjs/toolkit'

let cart = createSlice({
    name: 'cart',
    initialState: [
        {id : 0, name : 'White and Black', count : 2},
        {id : 2, name : 'Grey Yordan', count : 1}
      ],
    reducers: {
        increaseItem(state, action) {
            let i = state.findIndex((a)=>{return a.id === action.payload})
            state[i].count++
        },
        addItem(state, newItem) {
            state.push(newItem.payload)
        }
    }

})

export let { increaseItem, addItem } = cart.actions

export default configureStore({
    reducer: {
        cart: cart.reducer
    }
})
// Cart.js

import {Table} from 'react-bootstrap'
import { useDispatch, useSelector } from "react-redux"
import { increaseItem } from '../store.js'

function Cart() {

    let state = useSelector((state)=>{ return state })

    let dispatch = useDispatch()

    return (
        <div>
            <h6>{state.user.name} 의 장바구니</h6>
            <Table>
                <thead>
                    <tr>
                        <th>#</th>
                        <th>상품명</th>
                        <th>수량</th>
                        <th>변경하기</th>
                    </tr>
                </thead>
                <tbody>
                    {   // 반복문 쓸 때 key 속성 추가하면 좋다
                        state.cart.map((a, i) => 
                            <tr key={i}>
                                <td>{state.cart[i].id}</td>
                                <td>{state.cart[i].name}</td>
                                <td>{state.cart[i].count}</td>
                                <td>
                                    <button onClick={()=>{
                                       dispatch(increaseItem(state.cart[i].id))
                                    }}>+</button>
                                </td>
                            </tr>
                        )
                    }
                </tbody>
            </Table> 
        </div>
    )
}

export default Cart
// detail.js

<button className="btn btn-danger"
	onClick={()=>{
	dispatch(addItem({id : item.id, name : item.title, count : 1}))
	}}>주문하기</button>

 

 

※ 참고

 

- findIndex()

  • array 뒤에만 붙일 수 있는 함수
  • array.findIndex((a)=>{return 조건식}, 여기서 a 는 배열 안 하나하나의 데이터
  • array에 있던 자료를 다 꺼내서 조건식에 대입해보는데 조건식이 참이면 그게 몇번째 자료인지 일려줌

 

리액트에서 자주 쓰는 if 문 작성 패턴 5개

 

1. 컴포넌트 안에 쓰는 if / else

 

자바스크립트의 if 문은 return () 안의 JSX 내에서는 사용이 불가능하기 때문에 if 문을 사용하고 싶을 때는 컴포넌트를 만들어서 사용하면 된다. 

(<div> if (~~) {~~} </div> 불가능)

 

그래서 보통 return + JSX 전체를 뱉는 if 문을 작성해서 사용한다. 

function Component() {
  if ( true ) {
    return <p>참이면 보여줄 HTML</p>;
  } else {
    return null;
  }
}

 

 

참고로 깔끔한 코드를 작성하고 싶다면 else 문을 생략하는 방식을 사용해도 된다. 

function Component() {
  if ( true ) {
    return <p>참이면 보여줄 HTML</p>;
  } 
  return null;
}

 

 

2. JSX 안에서 쓰는 삼항연산자

 

영어로 ternary operator 라고 하는 삼항연산자는 아래 형식으로 사용하면 된다. if / else 와 달리 JSX 내에서 쓸 수 있다는게 장점이다. 

 

조건문 ? 조건문이 참일 때 실행할 코드 : 거짓일 때 실행할 코드

function Component() {
  return (
    <div>
      {
        1 === 1
        ? <p>참이면 보여줄 HTML</p>
        : null
      }
    </div>
  )
}

 

참고로 삼항연산자는 중첩해서도 사용할 수 있다. 하지만 깔끔한 코드는 아니기에 중첩해서 사용해야할 경우 if / else  문 사용을 추천한다. 

function Component() {
  return (
    <div>
      {
        1 === 1
        ? <p>참이면 보여줄 HTML</p>
        : ( 2 === 2 
            ? <p>안녕</p> 
            : <p>반갑</p> 
          )
      }
    </div>
  )
}

 

 

 

3. && 연산자로 if 역할 대신하기

true && false; // false
true && true; // true

true && '안녕'; // 안녕
false && '안녕'; // false
true && false && '안녕'; // false

 

&& 연산자는 처음으로 만나는 거짓인 피연산자 값을 반환하거나 모두 참일 경우 마지막 피연산자의 값을 반환한다. 

 

html 을 조건부로 보여줄 때 "만약 이 변수가 참이면 <p></p> 를 자리에 뱉고 참이 아니면 null 뱉고" 이런 식의 UI 를 만드는 경우 유용하게 쓸 수 있다. 아래의 예제의 두 컴포넌트는 동일한 기능을 하는데 && 연산자를 사용한 두번째 컴포넌트의 코드가 보기에 더 간편하다.

function Component() {
  return (
    <div>
      {
        1 === 1
        ? <p>참이면 보여줄 HTML</p>
        : null
      }
    </div>
  )
} 

function Component() {
  return (
    <div>
      { 1 === 1 && <p>참이면 보여줄 HTML</p> }
    </div>
  )
}

 

 

 

4. switch / case 조건문

 

if 문이 중첩해서 여러 개 달려있는 경우 가끔 쓴다. 

 

- switch 문 사용법

1. switch (검사할 변수) {}

2. case 검사할 변수가 이것과 일치하냐:

3. 일치한다면 case: 밑에 있는 코드 실행

4. default: 는 맨 마지막에 쓰는 else 문과 동일, 이것도 저것도 아니면 실행됨

 

if 문을 연달아 쓸 때 보다 코드가 약간 줄어들 수 있는데, 조건식란에서 변수를 하나만 검사할 수 있다는 것이 단점이다.

// if/else 

function Component2(){
  var user = 'seller';
  if (user === 'seller'){
    return <h4>판매자 로그인</h4>
  } else if (user === 'customer'){
    return <h4>구매자 로그인</h4>
  } else {
    return <h4>그냥 로그인</h4>
  }
}

// switch/case

function Component2(){
  var user = 'seller';
  switch (user){
    case 'seller' :
      return <h4>판매자 로그인</h4>
    case 'customer' :
      return <h4>구매자 로그인</h4>
    default : 
      return <h4>그냥 로그인</h4>
  }
}

 

 

 

5. object / array 자료형 응용

 

경우에 따라 다른 html 태그를 보여주고 싶은 경우 if 문 여러 개 또는 삼항연산자 여러 개 작성하는 경우가 일반적인데, 자바스크립트 object 자료형을 사용하는 방법도 있다. 

 

예를 들어 쇼핑몰 상품 설명 부분을 탭으로 만든다고 했을 때, 각 탭에 상품정보/배송정보/환불약관 내용을 보여주고 싶다. 

- 현재 state 가 info 면 <p>상품정보</p>

- 현재 state 가 shipping 면 <p>배송정보</p>

- 현재 state 가 refund 면<p>환불약관</p>

 

state 를 만들어놓고 if 문으로 state 를 검사하는 문법을 쓰는 것이 아닌, 자바스크립트 object 자료형에 보여주고 싶은 HTML 을 담는다.

그리고 마지막에 object 뒤에 [] 대괄호를 붙여 key 값이 현재상태인 자료를 뽑는다. 

 

그럼 현재상태라는 변수의 값에 따라 원하는 HTML 을 보여줄 수 있다. 

function Component() {
  var 현재상태 = 'info';
  return (
    <div>
      {
        { 
           info : <p>상품정보</p>,
           shipping : <p>배송관련</p>,
           refund : <p>환불약관</p>
        }[현재상태]
      }

    </div>
  )
}

 

 

아니면 변수에 저장해서 사용하는 방법도 있다. 

var 탭UI = { 
  info : <p>상품정보</p>,
  shipping : <p>배송관련</p>,
  refund : <p>환불약관</p>
}

function Component() {
  var 현재상태 = 'info';
  return (
    <div>
      {
        탭UI[현재상태]
      }
    </div>
  )
}

 

728x90
반응형