해결된 질문
23.07.14 16:39 작성
·
296
·
수정됨
1
각각의 todoReducer, memoReducer에 initialState를 전달하는 방법말고 createStore() 함수의 2번째 인자로 preloadedState를 전달하는 방법은 없나요?
const preloadedState = {
todo: initialTodoState,
memo: initialMemoState,
};
const store = createStore(
rootReducer,
preloadedState,
applyMiddleware(loggerMiddleware),
);
(추가) 위와 같이 작성 후(각 reducer의 초기 상태 전달 안함), 애플리케이션이 동작하지 않더라구요..!
(추가) 해서 GPT에게 물어봤습니다! 그랬더니 답변으로
이 방법을 사용하면 preloadedState
를 통해 전체 앱의 초기 상태를 한 곳에서 관리할 수 있어 유용합니다. 하지만 이 방법은 각 리듀서에서 초기 상태를 설정하는 방법과 병행해서 사용해야 합니다. 왜냐하면 preloadedState
는 앱이 시작될 때 한 번만 사용되고, 그 이후에는 각 리듀서에서 정의한 초기 상태가 사용되기 때문입니다.
해서 각 reducer에도 각각의 initialState 배열을 전달하니 동작은 하긴 하는데, 이럴꺼면 굳이 preloadedState
를 전달하는 의미가 없는 것 같아서 조금 헷갈리네요..!
결론적으로 질문은 아래와 같습니다..!
1) 각 reducer에게 초기 상태를 전달하지않고
combine한 reducer를 createStore에 전달할 때, 초기 상태를 전달해서 사용하는 방법이 있나요?
답변 1
1
2023. 07. 14. 19:23
안녕하세요, 소플입니다.
제가 질문을 제대로 이해한 것인지 모르겠지만, 일단 답변을 드려보겠습니다.
Redux Store를 생성할 때,
먼저 combineReducers()
함수를 사용해서 rootReducer
를 만들고,
그 이후에 createStore()
함수를 호출하게 됩니다.
그런데 combineReducers()
함수가 호출되는 시점에 Redux가 내부적으로 각 Reducer에 대해서 초기 상태 값을 수집하기 위해서 Action 호출을 하게 됩니다.
이 때도 Reducer는 역시 상태 값을 반환해야 하는데,
상태 값이 정의되어 있지 않기 때문에 따로 설정해주지 않으면 에러가 나게 됩니다.
그래서 각 Reducer의 state
파라미터에 아래와 같이 초기 상태값을 넣어주는 것입니다.
function todoReducer(state = todoInitialState, action) {
switch (action.type) {
case ACTION_TYPE_ADD_TODO:
return state.concat(action.text);
case ACTION_TYPE_REMOVE_TODO:
return state.slice(0, -1);
case ACTION_TYPE_REMOVE_ALL:
return [];
default:
return state;
}
}
그래서 질문하신 것처럼 각 Reducer에 초기 상태값을 전달하지 않고 preloadedState를 사용하는 방법은 함수가 호출되는 순서상 불가능하다 라고 보면 됩니다.
그리고 정확한 이해를 위해서 아래 코드와 같이 각 Reducer에 로그를 넣어서 호출해보시면 좋습니다.
<!DOCTYPE html>
<html>
<head>
<title>처음 만난 리덕스 - TODO</title>
<script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script>
</head>
<body>
<h3>오늘 할 일</h3>
<ul id="todo-list"></ul>
<div>
<input id="input-text" />
<button id="add-button">할 일 추가</button>
<button id="remove-button">할 일 삭제</button>
<button id="remove-all-button">모두 삭제</button>
<button id="logging-state">State Logging</button>
</div>
<h3>메모</h3>
<ul id="memo-list"></ul>
<div>
<input id="input-memo-text" />
<button id="add-memo-button">메모 추가</button>
<button id="remove-memo-button">메모 삭제</button>
</div>
<script>
// TODO 관련 Action Type
var ACTION_TYPE_ADD_TODO = 'ADD_TODO';
var ACTION_TYPE_REMOVE_TODO = 'REMOVE_TODO';
var ACTION_TYPE_REMOVE_ALL = 'REMOVE_ALL';
// MEMO 관련 Action Type
var ACTION_TYPE_ADD_MEMO = 'ADD_MEMO';
var ACTION_TYPE_REMOVE_MEMO = 'REMOVE_MEMO';
var todoInitialState = [];
var memoInitialState = [];
function todoReducer(state = todoInitialState, action) {
console.log('todoReducer', state, action);
switch (action.type) {
case ACTION_TYPE_ADD_TODO:
return state.concat(action.text);
case ACTION_TYPE_REMOVE_TODO:
return state.slice(0, -1);
case ACTION_TYPE_REMOVE_ALL:
return [];
default:
return state;
}
}
function memoReducer(state = memoInitialState, action) {
console.log('memoReducer', state, action);
switch (action.type) {
case ACTION_TYPE_ADD_MEMO:
return state.concat(action.text);
case ACTION_TYPE_REMOVE_MEMO:
return state.slice(0, -1);
default:
return state;
}
}
function loggerMiddleware({ getState }) {
return (next) => (action) => {
console.log('dispatch 예정 action', action);
// Middleware chain에 있는 다음 dispatch 함수를 호출
const returnValue = next(action);
console.log('dispatch 이후 state', getState());
return returnValue;
};
}
console.log("combineReducers")
var rootReducer = Redux.combineReducers({
todo: todoReducer,
memo: memoReducer,
});
var store = Redux.createStore(
rootReducer,
Redux.applyMiddleware(loggerMiddleware)
);
var todoListElem = document.getElementById('todo-list');
var memoListElem = document.getElementById('memo-list');
var inputElem = document.getElementById('input-text');
var inputMemoElem = document.getElementById('input-memo-text');
function render() {
// 이전 TODO, MEMO 목록 초기화
todoListElem.innerHTML = '';
memoListElem.innerHTML = '';
// TODO 목록 렌더링
store.getState().todo.forEach((todo) => {
const todoListItemElem = document.createElement('li');
todoListItemElem.textContent = todo;
todoListElem.appendChild(todoListItemElem);
});
// MEMO 목록 렌더링
store.getState().memo.forEach((memo) => {
const memoListItemElem = document.createElement('li');
memoListItemElem.textContent = memo;
memoListElem.appendChild(memoListItemElem);
});
}
render();
store.subscribe(render);
function addTodoActionCreator(text) {
return {
type: ACTION_TYPE_ADD_TODO,
text: text,
};
}
function removeTodoActionCreator() {
return {
type: ACTION_TYPE_REMOVE_TODO,
};
}
function removeAllActionCreator() {
return {
type: ACTION_TYPE_REMOVE_ALL,
};
}
function addMemoActionCreator(text) {
return {
type: ACTION_TYPE_ADD_MEMO,
text: text,
};
}
function removeMemoActionCreator() {
return {
type: ACTION_TYPE_REMOVE_MEMO,
};
}
document
.getElementById('add-button')
.addEventListener('click', function () {
// Action을 실제로 dispatch
store.dispatch(addTodoActionCreator(inputElem.value));
// Input 초기화
inputElem.value = '';
});
document
.getElementById('remove-button')
.addEventListener('click', function () {
store.dispatch(removeTodoActionCreator());
});
document
.getElementById('remove-all-button')
.addEventListener('click', function () {
store.dispatch(removeAllActionCreator());
});
document
.getElementById('logging-state')
.addEventListener('click', function () {
console.log('현재 state', store.getState());
});
document
.getElementById('add-memo-button')
.addEventListener('click', function () {
store.dispatch(addMemoActionCreator(inputMemoElem.value));
inputMemoElem.value = '';
});
document
.getElementById('remove-memo-button')
.addEventListener('click', function () {
store.dispatch(removeMemoActionCreator());
});
</script>
</body>
</html>
위와 같이 코드를 작성해서 애플리케이션을 실행하게 되면,
아래와 같은 로그가 처음에 출력되는 것을 볼 수 있습니다.
combineReducers()
함수가 호출되는 시점에 이미 Redux가 내부적으로 각 Reducer에 대해서 초기 상태 값을 수집하는 것이죠.
혹시 추가로 궁금한 부분이 있다면 댓글 달아주시기 바랍니다!
감사합니다.
2023. 07. 14. 20:26
와... reducer에서 log를 넣어서 확인하니까 넘 명확하고 좋네요!!!!!
매번 답변 잘해주셔서 넘넘 감사드립니다 ㅠㅠㅠ!!!