[React]Redux 시작하기
redux 이해하기
- redux는 props를 대신하여 사용하는 라이브러리다.
- 모든 컴포넌트들이 같은 값을 공유할 수 있게 만들어준다. 예를 들어 중첩 컴포넌트에서 props를 여러번 전송하지 않고 state를 직접 사용할 수 있다.
- state 데이터 관리가 용이하여 에러 추적 용이하다.
redux 설치하기
터미널에서 다음 명령어로 redux 라이브러리를 설치한다.
방법1. yarn add redux react-redux
방법2. npm install redux react-redux
redux 세팅하기
1.index.js에서 redux 라이브러리의 Provider를 import 한다.
import { Provider } from 'react-redux';
2.redux를 사용할 부분에 <Provider>로 감싸준다.
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<Provider>
<App />
</Provider>
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
);
3.createStore()안에 state를 저장한다.
import { createStore } from 'redux';
// 장바구니 데이터
let store = createStore(()=>{
return [
{ id : 0, name : 'White and Black', quan : 2},
{ id : 0, name : 'Red Knit', quan : 1}
]
});
4.<Provider>에서 state를 props로 전송한다.
<Provider store={store}>
<App />
</Provider>
redux 기본 사용방법
예제 만들기
장바구니 페이지를 만들어 redux를 적용해 보자.
1.cart.js 생성하여 부트스트랩에서 table UI 가져온다.
import React from 'react';
import { Table } from 'react-bootstrap';
const cart = () => {
return (
<div>
<Table responsive>
<thead>
<tr>
<th>#</th>
<th>상품명</th>
<th>수량</th>
<th>변경</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Table cell</td>
<td>Table cell</td>
<td>Table cell</td>
</tr>
</tbody>
</Table>
</div>
);
};
export default cart;
2.App.js에서 루트를 설정한다.
import Cart from './Cart';
<Route path="/cart">
<Cart></Cart>
</Route>
redux 사용하기 – state 공유하는 법
Cart.js에서 다음과 같이 코드를 작성한다.
1.redux의 state 데이터를 props로 변환해주는 함수를 만들어준다.
function state를props화(state){
return {
}
}
2.state 데이터를 props로 등록한다.
function state를props화(state){ // state : store에 있던 모든 데이터
return {
state : state // state라는 이름의 props
}
}
3.함수를 사용하여 export default connect()()한다.
import { connect } from 'react-redux';
export default connect(state를props화)(Cart)
//export default Cart;
4.props를 사용하여 데이터 바인딩한다.
const Cart = (props) => {
return (
<div>
<Table responsive>
<thead>
<tr>
<th>#</th>
<th>상품명</th>
<th>수량</th>
<th>변경</th>
</tr>
</thead>
<tbody>
{
props.state.map((a,i)=>{
return(
<tr key={i}>
<td>{ a.id }</td>
<td>{ a.name }</td>
<td>{ a.quan }</td>
<td>
<button className="btn btn-primary">+</button>
<button className="btn btn-danger">-</button>
</td>
</tr>
)
})
}
</tbody>
</Table>
</div>
);
};
redux 사용하기 – state 변경하는 법
1.데이터 수정 방법을 정의한 state 만든다.
- 데이터의 초기값을 변수에 담아준다.
- 데이터 수정 방법을 정의
- 데이터 수정 방법이 정의된 reducer를 데이터를 변수에 넣어서 사용한다.
index.js
// state 만들기
// 1. 데이터의 초기값을 변수에 담아준다.
let 초기값 = [
{ id : 0, name : 'White and Black', quan : 2},
{ id : 1, name : 'Red Knit', quan : 1}
];
// 2. 데이터 수정 방법을 정의
function reducer(state = 초기값, 액션){
if(액션.type === '수량증가'){
let copy = [...state];
copy[0].quan++;
return copy
}else if(액션.type === '수량감소'){
let copy = [...state];
if(copy[0].quan > 1){
copy[0].quan--;
}else{
alert("최소 1개 이상이어야 합니다.")
}
return copy
}else {
return state
}
}
// 3. 데이터 수정 방법이 정의된 reducer를 데이터를 변수에 넣어서 사용한다.
let store = createStore(reducer);
2.정의한 수정방법을 dispatch()를 사용하여 적용한다.
Cart.js
<button className="btn btn-primary" onClick={()=>{props.dispatch({type:'수량증가'})}}>+</button>
<button className="btn btn-danger" onClick={()=>{props.dispatch({type:'수량감소'})}}>-</button>
redux 사용하기 – state 여러개 사용하기
Combinereducers는 state 여러개 사용하기하기 위해서 사용하는 Hook이다.
- 두번째 state를 만들어준다.
- combineReducers를 사용하여 reducer를 모두 store에 담아준다.
- state 데이터를 props로 만들어주는 함수에 추가한 state를 추가한다.
index.js
import { combineReducers, createStore } from 'redux';
// 1. 데이터의 초기값을 변수에 담아준다.
let alert초기값 = true;
// 2. 데이터 수정 방법을 정의
function reducer2(state = alert초기값, 액션){
//function reducer2(state = true, 액션){
if(액션.type === '알림창닫기'){
state = false;
return state;
}
return state;
}
// 3. 데이터 수정 방법이 정의된 reducer를 데이터를 변수에 넣어서 사용한다.
let store = createStore(combineReducers({reducer, reducer2}));
Cart.js
function state를props화(state){
return {
state : state.reducer,
alert열렸니 : state.reducer2
}
}
{
props.alert열렸니 === true
?
<div className='my-alert-yellow'>
<p>지금 구매하시면 신규 할인 20%</p>
<button className='btn btn-danger' onClick={()=>{props.dispatch({type:'알림창닫기'})}}>닫기</button>
</div>
: null
}
redux 사용하기 – state 변경시 데이터 전송하는 법
key : 오브젝트
형태로 데이터를 담아서 전송하고 파라미터로 데이터를 받을 수 있다. 파라미터.key
형태로 전송받은 데이터를 사용할 수 있다.
데이터 전송
detail.js
<button className="btn btn-danger" onClick={()=>{
// payload에 데이터 담아보내기
props.dispatch({type:'항목추가', payload : { id:2, name: '새로운상품', quan:1} });
history.push('/cart');
}}>주문하기</button>
history.push
: 주소창에서 /cart로 이동하면 새로고침되기 때문에 push한 데이터가 표시되지 않는다. 항목추가 후에 바로 페이지 이동하여 추가된 데이터를 확인해본다.
detail.js
<button className="btn btn-danger" onClick={()=>{
props.dispatch({type:'항목추가', payload : { id:props.shoes[id].id, name: props.shoes[id].title, quan:1} });
history.push('/cart');
}}>주문하기</button>
임의의 데이터가 아닌 주문하기 버튼을 클릭한 해당 상품의 데이터를 전송하는 방법은 Detail.js 페이지에서 이미 사용되고 있는 데이터를 가지고 오면 된다.
detail.js
function reducer(state = 초기값, 액션){
if(액션.type === '항목추가'){
// findIndex : 배열 안에 원하는 데이터를 찾아주는 함수
let found = state.findIndex((a)=>{return a.id === 액션.payload.id});
if(found > 0){
let copy = [...state];
copy[found].quan++;
return copy;
}else {
let copy = [...state];
copy.push(액션.payload);
return copy;
}
}
이미 장바구니에 있는 상품이 추가되었을 경우 테이블을 새로 만들지 않고 기존에 있던 항목에 수량만 증가시킬 경우 state 수정 방법 정의에 조건을 걸어준다.
데이터 수신
index.js
function reducer(state = 초기값, 액션){
if(액션.type === '항목추가'){
let copy = [...state];
// paylaod 로 보낸 데이터 가져오기
copy.push(액션.payload);
return copy
}else if...
상품 데이터를 전송해서 각각의 장바구니 항목 수량 변경하기
현재 두번째 상품의 +, – 버튼을 클릭하면 첫번째 상품의 수량이 변경되도록 코딩되어 있습니다. 각각의 상품의 수량을 변경하도록 상품 데이터를 전송받는 코드를 작성합니다.
Cart.js
<tbody>
{
state.reducer.map((a,i)=>{
return(
<tr key={i}>
<td>{ a.id }</td>
<td>{ a.name }</td>
<td>{ a.quan }</td>
<td>
<button className="btn btn-primary" onClick={()=>{dispatch({type:'수량증가', data : a.id})}}>+</button>
<button className="btn btn-danger" onClick={()=>{dispatch({type:'수량감소', data : a.id})}}>-</button>
</td>
</tr>
)
})
}
</tbody>
index.js
function reducer(state = 초기값, 액션){
if(액션.type === '항목추가'){
let copy = [...state];
// paylaod 로 보낸 데이터 가져오기
copy.push(액션.payload);
console.log(copy);
return copy
}if(액션.type === '수량증가'){
let copy = [...state];
copy[액션.data].quan++;
return copy
}else if(액션.type === '수량감소'){
let copy = [...state];
if(copy[액션.data].quan > 1){
copy[액션.data].quan--;
}else{
alert("최소 1개 이상이어야 합니다.")
}
return copy
}else {
return state
}
}
redux 최신 사용법
useSelector와 useDispatcher를 사용하면 redux를 보다 간략한 코드로 작성할 수 있습니다. 현재는 Hook을 사용하여 코드를 작성하는 경우가 더 많습니다.
useSelector()
import { useSelector } from 'react-redux';
const Cart = (props) => {
let state = useSelector((state) => state ); // redux 안의 모든 state
...
{
state.reducer.map((a,i)=>{
return(
<tr key={i}>
<td>{ a.id }</td>
<td>{ a.name }</td>
<td>{ a.quan }</td>
<td>
<button className="btn btn-primary" onClick={()=>{props.dispatch({type:'수량증가'})}}>+</button>
<button className="btn btn-danger" onClick={()=>{props.dispatch({type:'수량감소'})}}>-</button>
</td>
</tr>
)
})
}
useDispatch()
import { useDispatch, useSelector } from 'react-redux';
const Cart = (props) => {
let state = useSelector((state) => state ); // redux 안의 모든 state
let dispatch = useDispatch();
...
<tbody>
{
state.reducer.map((a,i)=>{
return(
<tr key={i}>
<td>{ a.id }</td>
<td>{ a.name }</td>
<td>{ a.quan }</td>
<td>
<button className="btn btn-primary" onClick={()=>{dispatch({type:'수량증가'})}}>+</button>
<button className="btn btn-danger" onClick={()=>{dispatch({type:'수량감소'})}}>-</button>
</td>
</tr>
)
})
}
</tbody>