redux-toolkit으로 슬라이스를 만들고, dispatch로 initialState가 배열인 리덕스 값의 일부만 업데이트 하려는 중.
Cart.js
import { Button, Table } from 'react-bootstrap';
import { useDispatch, useSelector } from 'react-redux';
import { downCount, upCount } from '../module/store';
export default function Cart() {
const stock = useSelector((state) => state.stock);
console.log(stock);
const dispatch = useDispatch();
return (
<Table striped>
<thead>
<tr>
<th>상품번호</th>
<th>상품명</th>
<th>수량</th>
<th>변경하기</th>
</tr>
</thead>
<tbody>
{stock.map((stock) => (
<tr key={stock.id + 'cartT'}>
<td>{stock.id}</td>
<td>{stock.name}</td>
<td>{stock.count}</td>
<td>
<Button
type="button"
variant="info"
size="small"
style={{ marginRight: '10px' }}
onClick={dispatch(upCount())} {/* <--- 이 부분 /*}
>
+
</Button>
...
store.js
import { configureStore, createSlice } from '@reduxjs/toolkit';
const stock = createSlice({
name: 'stock',
initialState: [
{ id: 0, name: 'White and Black', count: 2 },
{ id: 2, name: 'Grey Yordan', count: 1 },
],
reducers: {
upCount(state) {
return state.count;
},
},
});
export const { upCount } = stock.actions; // 액션으로 export
export default configureStore({
reducer: {
stock: stock.reducer, // 리듀서 생성
},
});
Expected onClick listener to be a function, instead got a value of object type.
dispatch(action함수)
에서 action함수
가 object 타입이라서 발생한 에러다.
💡 initialState가 배열 내 객체다. 그래서 인덱싱을 먼저 해준 후에 key로 접근해야 한다.
컴포넌트에서 파라미터에 인덱스를 넘겨주고, 아래처럼 슬라이스를 수정했다.
const stock = createSlice({
name: 'stock',
initialState: [
{ id: 0, name: 'White and Black', count: 2 },
{ id: 2, name: 'Grey Yordan', count: 1 },
],
reducers: {
upCount(state, index) {
return state[index].count + 1; // <-- Error!
},
Cannot read properties of undefined (reading 'count')
파라미터로 넣어도 읽히지 않고, 개별 변수로 사용해야만 쓰인다.
💡 아마 이런 방식으로 쓰는 게 아닌 듯하다. 공식문서를 참고해보자.
import { createSlice } from '@reduxjs/toolkit'
const initialState = {
value: 0,
}
export const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (state) => {
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
incrementByAmount: (state, action) => {
state.value += action.payload
},
},
})
initialState가 객체일 땐 그냥 점 접근법으로 하면 된다. 사용할 때도 dispatch(액션함수())
이렇게 쓰면 되고.
💡 그럼 배열이 initialState일 때, 다른 말로 하자면 컴포넌트에서 dispatch를 통해 데이터를 파라미터로 넘겨줄 때 redux에서 어떻게 받아올지가 관건이다.
위 예시에서 보이듯 action
을 인자로, action.payload
에 데이터가 담긴다.
reducers: {
upCount(state, action) {
return (state[action.payload].count += 1);
},
하지만 이렇게 해도 에러는 발생하는데...
[Immer] An immer producer returned a new value and modified its draft. Either return a new value or modify the draft.
2번과 연결되는 이야기다. Redux-toolkit에서 객체 타입을 저장하면, return 하지 않고 직접 값을 변경할 수 있다.
ex) state.name = 'park'
그래서 action 함수를 통해서도 state에 직접 접근하려고 했던 건데, 엄밀히 말하자면 createSlice
함수로 상태를 생성하면 리덕스에 내장된 Immer.js에서 draft를 만들어 이를 통해 값을 관리한다.
개발자가 draft를 업데이트 하면, Immer에서 이러한 변경 사항을 추적하여 새로운 state를 만들고 이를 store에 반영한다. 이는 Redux 3원칙 중 하나인 '기존 state는 불변성 유지'를 위함이다.
그리고 마지막 에러. 이벤트 핸들러에 파라미터 있는 함수를 두려면 콜백 함수로 작성해야 한다. 함수명에 ()
를 붙이는 건 '실행'의 의미이기 때문이다.