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>
)
}
'웹 > 24-StudyWithPnP' 카테고리의 다른 글
[React+JS/리액트] 웹 스터디 11주차 (0) | 2024.07.12 |
---|---|
[React+JS/리액트] 웹 스터디 10주차 (0) | 2024.07.11 |
[React+JS/리액트] 웹 스터디 8주차 (0) | 2024.06.12 |
[React+JS/리액트] 웹 스터디 7주차 (0) | 2024.06.05 |
[React+JS/리액트] 웹 스터디 6주차 (2) | 2024.06.03 |