원인 1 : slice 안 reducer에 return을 써주지 않았다.
reducer의 반환값은 항상 새로운 상태여야 한다.
toolkit을 써주면 immer.js가 내장되어 있어
문법적으로 return이라고 표시하지 않아도 새로운 상태가 반환된다.
하지만 아래와 같이 혼재해서 써준 방식이 혼란이 되었다.
const todoSlice = createSlice({
name: 'todo',
initialState,
reducers : {
addTodo: (state,action) => {
state.push(action.payload);
// 이 부분은 immer가 새로운 상태를 자동 반환한다.
},
removeTodo: (state,action) =>{
return state.slice(0,-1);
// 이 부분에 return을 써주지 않으면 반환값이 없게 된다.
// 마지막 요소가 제외된 새로운 배열을 만들어주고 함수는 끝이 난다.
// 만약 immer를 여기에 적용하고 return을 안쓰려면
// state.pop()이라고 쓸 수 있다.
},
removeAll : (state,action) => {
return initialState;
// 여기도 state.length = 0; 이렇게 쓸수 있다.
},
},
});
원인 2 :
reducer의 함수를 선언해주는 부분에서 parameter로 action.payload가
필요하다고 썻지만 그 함수를 호출해주는 container의
dispatch 부분에 payload를 써주지 않았다.
dispatch를 다시 요약하자면 action을 호출하고 호출된 action이
상태를 바꾼다. 바뀐 상태는 store에 저장되고 단일 방향으로 흐르면서
상태를 관리해준다.
function mapDispatchToProps(dispatch,ownProps){
return {
addTodo: (text) =>{
dispatch(addTodoActionCreator(text));
//(text)가 빠졌음.
//dispatch(ThunkActionCreator(text));
},
removeTodo: ()=>{
dispatch(removeTodoActionCreator());
},
removeAll: ()=>{
dispatch(removeAllActionCreator());
},
//dispatch async function (will handle in asyncFunctionMiddleware)
triggerAsyncFunction: (asyncFunction)=>{
dispatch(asyncFunction);
}
}
}
전체코드
//1. configureStore로 스토어 생성
import { createStore, applyMiddleware, compose } from 'redux';
import { thunk } from 'redux-thunk';
import { configureStore } from '@reduxjs/toolkit';
import rootReducer from './reducers';
//const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
//const store = createStore(
// rootReducer,
// composeEnhancers(applyMiddleware(thunk))
//);
const store = configureStore({
reducer: rootReducer,
})
export default store;
//2. todoSlice createSlice로 액션, 리듀서를 써주고 각각 export
import { createSlice } from "@reduxjs/toolkit";
const initialState = []
const todoSlice = createSlice({
name: 'todo',
initialState,
reducers : {
addTodo: (state,action) => {
state.push(action.payload);
},
removeTodo: (state,action) =>{
state.slice(0,-1);
},
removeAll : (state,action) => {
return initialState;
},
},
});
export const {
addTodo,
removeTodo,
removeAll
} = todoSlice.actions;
export default todoSlice.reducer;
//3. 리듀서를 내보내는 루트 index 작성
import { combineReducers } from "redux";
//import todoReducer from "./todoReducers";
//import todoReducer from "../ducks/todoDucks"
import todoReducer from "../slice/todoSlice";
const rootReducer = combineReducers({
todo:todoReducer
});
export default rootReducer;
//4. 액션을 디스패치 해주는 컨테이너 생성
//container component (connect redux with react)
import { connect } from "react-redux";
import TodoApp from '../../components/TodoApp';
//import { addTodoActionCreator,
// removeTodoActionCreator,
// removeAllActionCreator } from "../actions";
//import { addTodoActionCreator,
//removeTodoActionCreator,
//removeAllActionCreator } from "../ducks/todoDucks";
import {
addTodo as addTodoActionCreator,
removeTodo as removeTodoActionCreator,
removeAll as removeAllActionCreator
} from "../slice/todoSlice"
//import ThunkActionCreator from '../thunks/addTodoThunk';
function mapStateToProps(state,ownProps){
return {todoItems:state.todo};
}
function mapDispatchToProps(dispatch,ownProps){
return {
addTodo: (text) =>{
dispatch(addTodoActionCreator());
//dispatch(ThunkActionCreator(text));
},
removeTodo: ()=>{
dispatch(removeTodoActionCreator());
},
removeAll: ()=>{
dispatch(removeAllActionCreator());
},
//dispatch async function (will handle in asyncFunctionMiddleware)
triggerAsyncFunction: (asyncFunction)=>{
dispatch(asyncFunction);
}
}
}
const TodoAppContainer = connect
(mapStateToProps, mapDispatchToProps)(TodoApp);
export default TodoAppContainer;