일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 타입스크립트
- 비트 연산자
- 파이어베이스
- Component
- axios
- 브라우저
- 리액트
- 알고리즘
- 자바스크립트
- Redux
- 프론트엔드
- array
- til
- localstorage
- TypeScript
- react localStorage
- JS
- CORS
- Frontend
- 프로그래머스
- firebase
- 프리온보딩
- v9
- 컴포넌트
- Reducer
- state
- es6
- JavaScript
- 원티드
- react
- Today
- Total
도리쓰에러쓰
[React] Redux4 :: dispatch로 데이터 보내기 본문
저번 게시물에 이어서 작성하겠습니다. (코드 참고)
1. dispatch()로 데이터 수정 요청할 때 데이터 보내기
1️⃣ Cart.js에서 버튼에 dispatch()에 payload 추가하기
dispatch({ type : 'type명', payload : '보낼데이터' })
<button onClick={()=>{ props.dispatch({ type : '수량증가', payload : {name:'kim'} }) }}>+</button>
2️⃣ reducer()에서 action.payload를 통해 데이터 사용하기
* 보낸 데이터는 action 파라미터에 저장되어 있습니다.
function reducer(state = basicState, action) {
if( action.type === '수량증가' ) {
console.log(action.payload);
return state;
} else {
return state
}
}
- console.log를 찍어보니 다음과 같이 데이터가 출력되는 것을 확인할 수 있습니다.
2. [주문하기] 버튼을 누르면 데이터에 상품 추가하기
- 이전에 만들어둔 Detail.js 화면이 있습니다.
* 접속 경로 : localhost:포트번호/detail/2
- 위 화면(사진)에서 [주문하기] 버튼을 클릭하면 index.js에 있는 basicState에 데이터가 추가되도록 하겠습니다.
* basicState 현재 데이터
let basicState = [
{ id : 0, name : 'Black Jacket', quan : 2 },
{ id : 1, name : 'White Bag', quan : 5 }
];
1️⃣ index.js에서 reducer()에 데이터 수정되는 방법 정의하기
function reducer(state = basicState, action) {
if( action.type === '항목추가' ) {
let copyArr = [...state];
copyArr.push();
return copyArr;
} else if( action.type === '수량증가' ) {
let copyArr = [...state];
copyArr[0].quan++;
return copyArr;
} else if( action.type === '수량감소' ) {
let copyArr = [...state];
copyArr[0].quan--;
return copyArr;
} else {
return state
}
}
- 기존 작성된 reducer()에서 조건이 '항목추가'일 때의 if문을 추가하였습니다.
- state 배열을 복사하여 복사한 배열에 ?? 값을 넣고, 복사한 배열을 return하는 if문입니다. ( ?? 값은 추후에 추가 예정 )
2️⃣ Detail.js에서 state를 prop화 해주는 것 작성하기
function store(state) {
return {
state : state.reducer,
alertState : state.reducer2
}
}
export default connect(store)(Detail)
// export default Detail;
- connect 함수도 import 해야 합니다.
import { connect } from 'react-redux';
3️⃣ [주문하기] 버튼 누르면 dispatch() 하여 데이터 함께 전송하기
<button className='btn btn-danger' onClick={ () => {
inventoryOut();
props.dispatch({ type : '항목추가' , payload : {id:2, name:'Black Jacket', quan:1} });
} }>주문하기</button>
4️⃣ index.js에서 reducer()에 데이터 추가하기
* reducer()의 action 파라미터는 dispatch()할 때 보낸 object입니다.
* 전송한 데이터를 사용하려면 : action.payload
function reducer(state = basicState, action) {
if( action.type === '항목추가' ) {
let copyArr = [...state];
copyArr.push(action.payload);
console.log(copyArr);
return copyArr;
} else if( action.type === '수량증가' ) {
let copyArr = [...state];
copyArr[0].quan++;
return copyArr;
} else if( action.type === '수량감소' ) {
let copyArr = [...state];
copyArr[0].quan--;
return copyArr;
} else {
return state
}
}
- 데이터가 저장되어 있는 action.payload를 배열 copyArr에 추가하였고, console창에 출력해보니 아래의 사진처럼 데이터가 추가된 것을 확인할 수 있습니다.
💡 개발환경에서 새로고침하면 redux도 초기화되기 때문에, localhost:포트번호/cart 경로로 접속하면 아래의 사진처럼 데이터가 초기화된 것을 확인할 수 있습니다.
- 개발환경에서 페이지 이동 시 강제 새로고침 안되게 하려면?
* Detail.js에서 [주문하기] 버튼에 history.push()를 추가하시면 됩니다. ( history.push()는 useHistory() Hook )
* history.push() : 페이지 이동을 강제로 시켜줍니다.
<button className='btn btn-danger' onClick={ () => {
inventoryOut();
props.dispatch({ type : '항목추가' , payload : {id:2, name:'Black Jacket', quan:1} });
history.push('/cart');
} }>주문하기</button>
- [주문하기] 버튼을 클릭하면,
localhost:포트번호/cart 로 이동되면서 데이터가 초기화되지 않고 나타나는 것을 확인할 수 있습니다.
🔔 위에 하드코딩으로 임시상품명 데이터를 추가시키지 않고 실제 상품명 데이터를 redux에 저장하기 (같은 상품이 이미 있으면 수량만 증가시키기)
1️⃣ Detail.js에 간단한 form 만들기
<div>
<br /> <br />
ID : <input type="number"></input> <br /> <br />
제품명 : <input type="text"></input> <br /> <br />
재고 : <input type="number"></input> <br /> <br />
<button className='btn btn-dark'>주문하기</button>
</div>
2️⃣ useState()를 이용하여 변수 3개를 생성하기
let [idVal, setIdVal] = useState(0);
let [nameVal, setNameVal] = useState('');
let [quanVal, setQuanVal] = useState(0);
3️⃣ onChange() 이벤트리스너를 통해 input 태그에 입력되는 값을 변수에 저장하기
ID : <input type="number" onChange={ (e) => { setIdVal(e.target.value) } }></input> <br /> <br />
제품명 : <input type="text" onChange={ (e) => { setNameVal(e.target.value) } }></input> <br /> <br />
재고 : <input type="number" onChange={ (e) => { setQuanVal(e.target.value) } }></input> <br /> <br />
4️⃣ onClick() 이벤트리스너를 통해 dispatch()로 데이터를 넘기고,
localhost:포트번호/cart 경로로 강제 페이지 이동을 시켜주기
<button className='btn btn-dark' onClick={ () => {
props.dispatch({
type : '주문하기',
payload : {
id : idVal,
name : nameVal,
quan : quanVal
}
});
history.push('/cart');
} }>주문하기</button>
5️⃣ index.js에서 아래 코드과 같이 작성하기
function reducer(state = basicState, action) {
if( action.type === '항목추가' ) {
// 위 코드 참고
} else if( action.type === '주문하기' ) {
let copyArr = [...state];
action.payload.id = Number(action.payload.id);
action.payload.quan = Number(action.payload.quan);
for(let i = 0; i < state.length; i++) {
if( copyArr[i].id === action.payload.id ) {
copyArr[i].quan++;
break;
} else if(i === state.length-1) {
copyArr.push(action.payload);
}
}
return copyArr;
} else if( action.type === '수량증가' ) {
// 위 코드 참고
} else if( action.type === '수량감소' ) {
// 위 코드 참고
} else {
return state
}
}
- [...state] : 배열 복사
- if문 : copyArr에 있는 id와 action.payload.id(input에서 입력했던 ID)가 일치하면 재고(quan) +1
- else문 : copyArr에 있는 id와 action.payload.id(input에서 입력했던 ID)가 일치하지 않으면 copyArr에 데이터 추가
- Detail.js에서 form창에 다음과 같이 입력하면,
- localhost:포트번호/cart로 페이지가 이동되면서 리스트가 추가된 것을 확인할 수 있습니다.
- 이전 페이지로 돌아가서, form창에 다음과 같이 입력하면
- localhost:포트번호/cart로 페이지가 이동되면서,
ID가 1인 항목이 있기 때문에 재고 수가 1개 추가된 것을 확인할 수 있습니다.
3. 전체 코드
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { combineReducers, createStore } from 'redux';
let alertState = true;
function reducer2(state = alertState, action) {
if( action.type === 'alertClose' ) {
state = false;
return state;
} else {
return state;
}
}
let basicState = [
{ id : 0, name : 'Black Jacket', quan : 2 },
{ id : 1, name : 'White Bag', quan : 5 }
];
function reducer(state = basicState, action) {
if( action.type === '항목추가' ) {
let copyArr = [...state];
copyArr.push(action.payload);
console.log(copyArr);
return copyArr;
} else if( action.type === '주문하기' ) {
let copyArr = [...state];
action.payload.id = Number(action.payload.id);
action.payload.quan = Number(action.payload.quan);
for(let i = 0; i < state.length; i++) {
if( copyArr[i].id === action.payload.id ) {
copyArr[i].quan++;
break;
} else if(i === state.length-1) {
copyArr.push(action.payload);
}
}
return copyArr;
} else if( action.type === '수량증가' ) {
let copyArr = [...state];
copyArr[0].quan++;
return copyArr;
} else if( action.type === '수량감소' ) {
let copyArr = [...state];
copyArr[0].quan--;
return copyArr;
} else {
return state
}
}
// let store = createStore(reducer);
let store = createStore(combineReducers({reducer, reducer2}));
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<Provider store={store}>
<App />
</Provider>
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
Detail.js
import React, { useContext, useEffect, useState } from 'react';
import { Nav } from 'react-bootstrap';
import { useHistory, useParams } from 'react-router-dom';
import styled from 'styled-components';
import { inventoryContext } from './App.js';
import './Detail.scss';
import { CSSTransition } from "react-transition-group";
import { connect } from 'react-redux';
let Box = styled.div`
padding : 20px;
`;
let Title = styled.h4`
font-size : 25px;
color : ${ props => props.color }
`;
function Detail(props) {
let { id } = useParams();
let history = useHistory();
let findProduct = props.products.find(function(product) {
return product.id = id;
});
let [alert, setAlert] = useState(true);
let [inputData, setInputData] = useState('');
let [pushTab, setPushTab] = useState(0);
let [switchBtn, setSwitchBtn] = useState(false);
let [idVal, setIdVal] = useState(0);
let [nameVal, setNameVal] = useState('');
let [quanVal, setQuanVal] = useState(0);
useEffect(()=>{
let timer = setTimeout(() => { setAlert(false) }, 2000);
return ()=>{ clearTimeout(timer) }
}, []);
function inventoryOut() {
let id = findProduct.id;
let copyArr = [...props.inventory];
copyArr[id] = copyArr[id] - 1;
props.setInventory(copyArr);
}
let inventory = useContext(inventoryContext);
return(
<div className='container'>
<Box>
<Title className='red'>상세페이지</Title>
</Box>
<input onChange={ (e)=>{ setInputData(e.target.value) } }/>
{
alert === true
? (<div className='my-alert-red'>
<p>재고가 얼마 남지 않았습니다!</p>
</div>)
: null
}
<div className='row'>
<div className='col-md-6'>
<img className='img' src={ process.env.PUBLIC_URL + '/images/img'+ (Number(findProduct.id) + 1)+'.jpg' } />
</div>
<div className='col-md-6 mt-4'>
<h4 className='pt-5'>{ findProduct.title }</h4>
<p>{ findProduct.content }</p>
<p>{ findProduct.price }</p>
<Info id={ findProduct.id } inventory={ props.inventory }> </Info>
<button className='btn btn-danger' onClick={ () => {
inventoryOut();
props.dispatch({
type : '항목추가' ,
payload : {
id : 2,
name : 'Black Jacket',
quan : 1
}
});
history.push('/cart');
} }>주문하기</button>
<br />
<br />
<button className='btn btn-danger' onClick={ () => {
history.goBack();
}}>뒤로가기</button>
</div>
<div>
<br /> <br />
ID : <input type="number" onChange={ (e) => { setIdVal(e.target.value) } }></input> <br /> <br />
제품명 : <input type="text" onChange={ (e) => { setNameVal(e.target.value) } }></input> <br /> <br />
재고 : <input type="number" onChange={ (e) => { setQuanVal(e.target.value) } }></input> <br /> <br />
<button className='btn btn-dark' onClick={ () => {
props.dispatch({
type : '주문하기',
payload : {
id : idVal,
name : nameVal,
quan : quanVal
}
});
history.push('/cart');
} }>주문하기</button>
</div>
</div>
<Nav className="mt-5" variant="tabs" defaultActiveKey="link-0">
<Nav.Item>
<Nav.Link eventKey="link-0" onClick={ ()=>{ setSwitchBtn(false); setPushTab(0) } }>상품설명</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey="link-1" onClick={ ()=>{ setSwitchBtn(false); setPushTab(1) } }>배송정보</Nav.Link>
</Nav.Item>
</Nav>
<CSSTransition in={ switchBtn } classNames="wow" timeout={500}>
<TabContent pushTab={ pushTab } setSwitchBtn={ setSwitchBtn }/>
</CSSTransition>
</div>
)
}
function TabContent(props) {
useEffect(()=>{
props.setSwitchBtn(true);
});
if(props.pushTab === 0) {
return <div>0번째 내용</div>
} else if(props.pushTab === 1) {
return <div>1번째 내용</div>
} else if(props.pushTab === 2) {
return <div>2번째 내용</div>
}
}
function Info(props) {
return(
<p> 재고 : { props.inventory[props.id] }</p>
)
}
function store(state) {
return {
state : state.reducer,
alertState : state.reducer2
}
}
export default connect(store)(Detail)
// export default Detail;
* 이외의 코드는 이전 게시물에 작성된 코드와 일치합니다.
'코딩애플 (React) > 기초수업(코딩애플) - 3' 카테고리의 다른 글
[React] shop 홈페이지 기능 완성하기 가이드 (0) | 2022.02.01 |
---|---|
[React] Redux5 :: useSelector, useDispatch (0) | 2022.02.01 |
[React] Redux3 :: state와 reducer가 더 필요하다면? (0) | 2022.01.31 |
[React] Redux2 :: reducer / dispatch로 데이터 수정하기 (0) | 2022.01.26 |
[React] Redux1 :: props 대신 사용하기 (0) | 2022.01.26 |