기존에 리액트를 사용하면서
컴포넌트가 많아짐에 따라 state 관리가 힘들어진다는 걸 느낀 적이 있다.
여러 컴포넌트에서 같이 사용하거나,
부모의 부모 컴포넌트에서 사용되는 state를 물려 받아야 한다면
이를 props로 전달해주는 것은 무척 번거롭다...(예전에 투두메이트 클론코딩을 하다가 집어던진 이유도 이거였다ㅠ)
Redux는 전역적으로 state를 관리하게 도와준다.
https://kyun2da.dev/%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC/Redux-%EC%A0%95%EB%A6%AC/
Redux 정리
Redux란? 리덕스는 리액트에서 가장 많이 사용되는 상태 관리 라이브러리중 하나이다. 리덕스를 사용하면 컴포넌트의 상태 업데이트 관련 로직을 다른 파일로 분리시켜서 효율적으로 관리할 수
kyun2da.dev
- 액션(Action)
{ type: 'ADD_TODO', data: { id: 1, text: '리덕스 배우기' } }
상태에 어떠한 변화가 필요하면 action이 발생한다. 이는 하나의 객체로 표현된다.
액션객체는type필드를 반드시 가지고 있어야 한다. 이값을 액션의 이름이라고 생각하면 되고, 나머지 값들은 나중에 상태 업데이트를 할 때 참고해야 할 값이며 작성자 마음대로 넣을 수 있다 - 리듀서(reducer)
const initialState = { counter: 1, } function reducer(state = initialState, action) { switch (action.type) { case INCREMENT: return { counter: state.counter + 1, } default: return state } }
- 스토어(Store)
스토어안에는 현재 어플리케이션 상태와 리듀서가 들어가 있으며 그 외에도 몇 가지 중요한 내장 함수를 지닌다. 하나의 프로젝트는 하나의 스토어만 가질 수 있다. - 디스패치(Dispatch)
스토어의 내장 함수 중 하나인 디스패치는 액션 객체를 넘겨줘서 상태를 업데이트 하는 유일한 방법이다. 이 함수가 호출되면 스토어는 리듀서 함수를 실행시켜서 새로운 상태를 만들어 준다
실습
store.js
import { configureStore, createSlice, current } from "@reduxjs/toolkit";
import data from "./data";
import { React, useEffect, useState } from 'react'
// { name : 'state Name', initialState : 'state Value'}
let user = createSlice({ //useState() 역할임
name : 'user',
initialState : '도마야', // user='도마야'
reducers : {
changeName(state){
return 'DOMAYA'
}
}
})
export let {changeName} = user.actions; //state변경함수
let peple = createSlice({
name : 'peple',
initialState : 200 // peple = 200
})
let stock = createSlice({ // [stock, setStock] = useState([7,13,20]);
name : 'stock',
initialState : [7,13,20], // stock = [7,13,20]
reducers : {
changeStock(state, action){
console.log(action)
let copy = [...state]
let arr =[];
if(action.payload == 'up'){
copy.map((item)=>{item++; arr.push(item)})
return arr;
}else{
copy.map((item)=>{item--; arr.push(item)})
return arr;
}
}
}
})
export let {changeStock} = stock.actions;
let cart = createSlice({
name : 'cart',
initialState :
[ // cart = [{},{}]
{ id : 0, title : "Black and White", count: 2},
{ id : 0, title : "Red Knit", count: 1}
],
reducers : {
changeCart(state, action){
// console.log(state);
console.log(action)
let clothes = [...state];
clothes.push(data[action.payload])
return clothes;
},
changeCount(state, action){
if(state[action.payload[0]].count > 0){
state[action.payload[0]].count += action.payload[1];
}
}
}
})
export let {changeCart, changeCount} = cart.actions;
export default configureStore({ // 상태변수 등록 하는 부분
reducer: {
user : user.reducer,
peple : peple.reducer,
stock : stock.reducer,
cart : cart.reducer
}
})
detail.js
이 컴포넌트에서는 장바구니 버튼을 누르면 장바구니 state가 업데이트되어야 한다.
import { React, useEffect, useState } from 'react'
import { useParams, useNavigate } from 'react-router-dom';
import Tab from 'react-bootstrap/Tab';
import Tabs from 'react-bootstrap/Tabs'
import { useDispatch, useSelector } from "react-redux";
import { changeCart } from './../store';
function Detail(props) {
let value = useSelector((state) => state)
console.log(value);
let dispatch = useDispatch(); //actions에 있는 걸 호출해주는
let { id } = useParams(); //HOOK
let findId = props.data.find((item) => item.id == id);
const navigate = useNavigate();
let [count, setCount] = useState(0);
const [flag, setflag] = useState(true);
let [clickTab, setClickTab] = useState(0);
return (
<>
<div className='container'>
<div className='row'>
<div className='col-md-12 mt-4'>
<img src={findId.img} className='img-fluid' alt='yb' />
<h4>{findId.title}</h4>
<p>{findId.content}</p>
<p>{findId.price}원</p>
<button className="btn-danger">주문하기</button>
<button className='btn-warning' onClick={() => { navigate(-1) }} >뒤로가기</button>
<button className='btn-primary' onClick={() => { navigate('/') }} >홈</button>
<button className='btn-dark' onClick={() => { dispatch(changeCart(id));navigate('/cart') }}>장바구니</button>
</div>
</div>
</div>
</>
)
}
export default Detail;
react-redux로부터
를 해주고,
store에 만들어둔 리듀서를 임포트해야 한다.
그리고 장바구니 버튼을 클릭하면 상품이 담기도록, 해당 리듀서를 실행시켜야 한다.
그러기 위해 dispatch를 선언하고,
버튼에는 onclick으로
이렇게...
<button className="btn-sm btn-dark" onClick={()=>{dispatch(changeCount([index,1]))}}>+</button>
<button className="btn-sm btn-dark" onClick={()=>{dispatch(changeCount([index,-1]))}}>-</button>
Cart.js에 있는 이 두 버튼은
클릭하면 수량을 늘리거나 줄이는 역할을 한다.
저 플러스 마이너스 버튼을 누를 때마다 store.js에 있는 리듀서가 호출된다.
let cart = createSlice({
name : 'cart',
initialState :
[ // cart = [{},{}]
{ id : 0, title : "Black and White", count: 2},
{ id : 0, title : "Red Knit", count: 1}
],
reducers : {
changeCart(state, action){
// console.log(state);
console.log(action)
let clothes = [...state];
clothes.push(data[action.payload])
return clothes;
},
changeCount(state, action){
if(state[action.payload[0]].count > 0){
state[action.payload[0]].count += action.payload[1];
}
}
}
})
export let {changeCart, changeCount} = cart.actions;
저 changeCount가 해당 리듀서임
action.payload에는 index와 증감값이 들어간다.
배열 형태로 들어가기 때문에
action.payload[0]에는 인덱스가, action.payload[1]에는 +1 혹은 -1이 들어간다.
'개발' 카테고리의 다른 글
230102 : WebSocket (0) | 2023.01.02 |
---|---|
Request Param으로 배열 처리 (0) | 2022.12.31 |
리액트 4일차 : context API, axios (0) | 2022.12.30 |
에러일기 : com.sun.mail.smtp.SMTPSenderFailedException: 550 5.7.0 Restricted by spam policy (0) | 2022.12.29 |
에러 : JavaMailSenderImpl cannot be resolved to a type (0) | 2022.12.29 |