store(Redux)에 저장된 state가 array, object 자료인 경우 state 변경을 좀 쉽게 편리하게 할 수 있습니다.
redux state가 array/object인 경우 변경하려면
아래의 state에서 kim을 park으로 변경하고 싶으면
let user = createSlice({
name : 'user',
initialState : {name : 'kim', age : 20},
reducers : {
changeName(state){
return {name : 'park', age : 20}
}
}
})
당연히 저렇게 쓰면 changeName()사용시 변경됩니다.
근데 state를 직접 수정하라고해도 변경 잘 됩니다.
state를 직접 수정하는 문법을 사용해도 잘 변경되는 이유는
Immer.js 라이브러리가 state 사본을 하나 더 생성해준 덕분인데 Redux 설치하면 딸려와서 그렇습니다.
let user = createSlice({
name : 'user',
initialState : {name : 'kim', age : 20},
reducers : {
changeName(state){
state.name = 'park'
}
}
})
그래서 결론은 array/object 자료의 경우 state 변경은 state를 직접 수정해버려도 잘 되니까 직접수정해도 됩니다.
버튼을 하나 만들고 그 버튼을 누르면 위에 있는 state 중에
age 항목이 +1 되어야 합니다.
코드를 짜보면 increase라는 함수 만들고 export하고 필요한곳에서 import해서 dispatch(increase()) 하면
+1 됩니다.
let user = createSlice({
name : 'user',
initialState : {name : 'kim', age : 20},
reducers : {
increase(state){
state.age += 1
}
}
})
state 변경함수가 여러개 필요하면
방금 +1기능을 만들었는데 만약 +10 +100을 하고 싶으면
각각의 함수를 만들면되는데 함수는 파라미터문법 이용하면 비슷한 함수 여러개 만들 필요가 없어집니다.
state 변경함수의 둘째 파라미터를 작명하면 이제 increase(10), increase(100) 이런 식으로
파라미터을 입력해서 함수사용이 가능합니다. 파라미터자리에 넣은 자료들은 a.payload하면 나옵니다.
let user = createSlice({
name : 'user',
initialState : {name : 'kim', age : 20},
reducers : {
increase(state, a){
state.age += a.payload
}
}
})
(참고)
- a 말고 aciton이런식으로 작명 많이 합니다
- action.type 하면 state변경함수 이름이 나오고
- action.payload 하면 파라미터가 나옵니다.
응용
응용 1. +버튼을 누르면 해당 상품의 수량부분이 +1 되는 기능
해당기능을 구현하기 위해 순서대로 생각해보았습니다.
- 먼저 store(Redux)에서 상품의 수량을 증가시키는 함수 작성
- 현재 상품의 id를 매개변수로 store(Redux)에 전달
- store(Redux)에서 해당 상품의 id를 매개변수로 받아 id와 일치하는 상품 찾기
- 찾은 상품의 count+1
현재 페이지의 모습입니다. 상품옆에 +버튼을 클릭하면 해당 상품의 수량이 +1증가되어야 하는데
+버튼을 누를때 현재 상품의 id값을 store(Redux)에 전달해서 id값이랑 상품이랑 일치하면 상품의 count를 +1 증가시켜주면됩니다.
id를 전달해줍니다.
표 부분인데 dispatch부분에서 매개변수로 상품의 id를 전달해줬습니다.
핵심은 이 코드입니다. dispatch(upCount(state.stock[i].id))
<tbody>
{
state.stock.map(function (a,i) {
return (
<tr key={i}>
<td>{state.stock[i].id}</td>
<td>{state.stock[i].name}</td>
<td>{state.stock[i].count}</td>
<td>
<button onClick={()=>{
dispatch(upCount(state.stock[i].id))
}}>+</button>
</td>
</tr>
)
})
}
</tbody>
그리고 store(Redux)에서 해당 id로 상품을 찾고 count를 +1해줬습니다.
해당 상품을 찾는과정에서 고민을 하다가 map(반복문)을 이용해서 상품의 인덱스랑 파라미터가 일치하면 +1해줬습니다.
let stock = createSlice({
name : 'stock',
initialState : [
{id : 0, name : 'White and Black', count : 2},
{id : 2, name : 'Grey Yordan', count : 1}
],
reducers:{
upCount(state, a){
//console.log(a);
state.map((temp,index)=>{
//console.log(temp.id);
if(temp.id==a.payload)
{
console.log(temp.id , a);
temp.count=temp.count+1;
}
})
return state
}
}
})
확인해보면 잘 작동합니다.
저는 map(반복문)을 이용해서 상품을 찾았는데
findIndex를 이용해서도 찾을 수 있습니다.
let 번호 = state.findIndex((a)=>{ return a.id === action.payload } ) -> 번호는 0,1
state[번호].count++
2. DetailPage에서 주문하기 버튼을 누르면 새로운 상품이 state에 추가되는 기능
아래는 디테일페이지인데 여기서 주문하기 버튼을 누르면 해당 상품이 store(Redux)에 추가되는 기능입니다.
생각한 구현방법입니다.
- store(Redux)에서 상품을 추가하는 함수 작성
- 상품의 정보 store(Redux)에 매개변수로 전달
- 매개변수로 받아서 추가
먼저 Redux(store)에서 상품을 추가하는 함수를 작성했습니다.
...
addStock(state, a){
let temp = {id : a.payload.id, name : a.payload.title, count : 1}
return state.concat(temp);
}
이제 상품을 보내주겠습니다.
주문하기 버튼을 클릭했을때 dispatch를 이용해 해당상품을 보내주었습니다.
id는 useParams()값입니다.
...
<button className="btn btn-danger" onClick={()=>{
console.log(props.shoes[id]);
dispatch(addStock(props.shoes[id]));
}}>주문하기</button>
그래서 확인해보면
처음 접속했을때의 화면입니다.
주문하기 버튼을 클릭해보고 확인버튼을 눌러 store(Redux)의 데이터를 확인해보겠습니다.
확인 -> 주문하기 -> 확인을 한 결과 값이 제대로 들어갑니다.
잘 구현하였는데 여기서 드는 의문은
concat을 이용해 값을 넣어주었는데
return state.concat(temp)을
state.push(temp) 를 이용해도 잘 들어가진다.
두 메소드의 같은 점은 배열안에 요소를 추가해준다는 점인데. 다른점은 리턴 되는 값이 다르다는 것이다.
concat은 원본 배열을 유지한채 배열을 반환해주고
push는 리턴값이 새롭게 생성된 배열안의 요소의 개수이다.
두메서드의 차이를 보다가 React의 불변성에 대해 알게되었다.
React의 가장 큰 특징은 불변성이다.
push를 하게 되면 state 내부의 값을 직접수정하게 되고 React는 state를 기준으로 변할 때마다 렌더링하기 때문에 push를 하면 변동사항을 알 수 없다. 그렇기 때문에 concat 혹은 spread operator를 사용해야한다. 불변성을 유지해야 성능도 최적화 할 수 있기 때문이다.
응용3 중복삼품은 추가 x
- store(Redux)의 상품추가해주는함수에서 장바구니에 추가하는 상품이 이미 존재하면 해당 상품 수량 +1
- 함수에서 map을 이용해 상품을 돌면서 추가할려는 상품이 있으면 count+1 해주고 없으면 새로추가
먼저 함수의 코드입니다. map을 이용해 check를 해주었습니다.
상품이 있으면 check=true가 되고 해당 상품의 개수를 +1
상품이 없으면 check=false가 되고 새로운 객체생성하여 concat으로 기존상품에 붙여주기
addStock(state, a){
let check = false;
//console.log("addstock : " + a.payload.title)
state.map((x,index)=>{
//상품이 있으면 +1
if(x.id==a.payload.id)
{
check = true;
x.count=x.count+1;
return state;
}
})
if(check==false) // check=false이면 장바구니에 해당상품 없음
{
let temp = {id : a.payload.id, name : a.payload.title, count : 1}
return state.concat(temp);
}
//state.push(temp);
}
처음화면입니다. 이미 존재하는 상품을 추가해보겠습니다.
주문하기 버튼을 클릭하면
해당 상품의 수량이 +1 됩니다.
그리고 없는 상품을 추가해보겠습니다. Red Knit라는 새로운 상품이 잘 추가되었습니다.
응용4 장바구니 항목 삭제기능
+버튼 옆에 삭제 버튼을 만들어 해당 상품을 삭제해보겠습니다.
- store(Redux)에서 삭제함수 만들기
- dispatch로 해당상품을 파라미터로 전달
- 전달받은 상품의 id값을 map을 이용해 일치하면 splice메서드 이용해서 삭제
삭제함수입니다.(중간에 콘솔은 값확인할려고한거니 삭제 또는 무시해도 됩니다)
해당상품을 if로 이용해 찾으면 spice를 이용해 해당상품을 삭제하였습니다.
deleteStock(state,a){
console.log(a.payload);
state.map((x,index)=>{
console.log(x.id);
if(x.id==a.payload.id)
{
console.log(x);
state.splice(x,1)
}
})
}
아래코드에서 dispatch를 이용해 상품을 전달해주었습니다.
<td>
<button onClick={()=>{
dispatch(deleteStock(state.stock[i]))
}}>삭제</button>
</td>
확인해보겠습니다.
처음화면이고 이제 0번상품을 삭제해보겠습니다.
잘 삭제가 되는 것을 볼 수 있습니다.
'프론트엔드 > react' 카테고리의 다른 글
React - localStorage (0) | 2023.08.05 |
---|---|
React - if문 (0) | 2023.08.05 |
React - Redux(3) Redux의 state변경하는법 (0) | 2023.08.05 |
React - Redux(2) store에 state 보관하고 쓰는 법 (0) | 2023.08.04 |
React - Redux(1) (0) | 2023.08.04 |