Cannot assign to read only property 'isDone' of object
#<Object>
reduxToolkit을 사용 하여 todoList를 만들다가
// todoList Component
const todoList = useSelector(state=>state.todoList)
const newTodoList = todoList.map((target) => {
if (target) {
target.isDone = !target.isDone;
return target;
}
});
// Cannot assign to read only property 'isDone' of object '#<Object>'
이러한 에러가 발생하였다.
reducer의 state
는 읽기만 해야지 직접적으로 바꾸는 값이 없어야 한다.
reducer 함수 안에는 immerjs
가 내장되어 있으므로
// todoListReducer
isDoneTodo: (state, { payload }) => {
const newTodoList = state.map((target) => {
if (target.id === payload) {
target.isDone = !target.isDone;
return target;
}
return target;
});
return newTodoList;
},
이렇게 하여서 문제를 해결하려 했지만..
[Immer] An immer producer returned a new value and modified its draft. Either return a new value or modify the draft.
이러한 에러가 떴다.
reduxToolkit
에는 immer가 내장되어있고 immer로 인해 state 값을 복사한 Proxy
를 가지고 우리가 push
를 해도 새로운 state 변경으로 받아들여 reRendering
이 일어나게끔 도와주는 고마운 녀석이다.
그렇지만
저 위의 에러를 해석해보면
reduxToolkit에서
reducer안에서 사용 할 때 새로운 값을 return
해주거나 아니면 draft
를 수정해주어라는 의미인데
draft
공식적으로 proxy라고 immer에서 이야기하지만
draft
=== proxy이다. 즉, state를 immer에서 자동 복사해주는데,
복사된 state = proxy = draft라고 생각하면 된다.
// draft를 modified 해주라는 의미는
state.isDone = false // 이러한 식으로 말이다.
이런식으로 간단하게 수정해줘~~ 우리가 바뀐값으로 처리해서 return 해줄께 이런 말 뜻임
다시 본론으로 돌아와서
// todoListReducer
isDoneTodo: (state, { payload }) => {
const newTodoList = state.map((target) => {
if (target.id === payload) {
target.isDone = !target.isDone; // 여기가 문제임
return target;
}
return target;
});
return newTodoList;
},
React에서 map 함수는 새로운 배열을 return 해준다. 새로운 배열을 return 해주어야 하는데 나는 지금
Reducer안에서 map함수로 돌려지고 있는 요소에 "직접적으로" 수정을 하려고 하기 때문에 저러한 에러가 발생하는 것이다.
=> Array.map이라는 메소드는 원본을 지킨다. 지금 위 코드는 원본을 바꾸려고 하기 때문이다.
return 하려는 '값'을 얕은 복사본을 만들어서 return 시켜주자
component 안에서 무언가 reducer의 state 값을 변경하고 싶다면,
// todoList Component
const todoList = useSelector(state=>state.todoList)
const newTodoList = todoList.map((target) => {
if (target.id === id) {
// target.isDone = !target.isDone;
// return target; 이렇게 말고
return { ...target, isDone: !target.isDone };
}
return target;
});
reducer안에서 정의된 함수안에서 해주고 싶다면
// todoListReducer
isDoneTodo: (state, { payload }) => {
const newTodoList = state.map((target) => {
if (target.id === payload) {
//target.isDone = !target.isDone;
return target;
}
return { ...target, isDone: !target.isDone };
});
return newTodoList;
},
내 문제점은 map에서 return 되는 값을 수정해주는 것이 아닌 순회하고 있는 요소를 변경하려고 해서 문제가 생기는 것이 point이다.
map 함수는 배열의 원본
은 건들지 않고 새로운 배열을 return
하는데,... 원본을 변경시키려고 하니까 계속해서 에러가 생기는 것. 사실 계속해서 저런식으로 원본의 요소를 건드려서 저런 에러가 나서 deepCopy 함수 만들어서 변경시켰음 ;;;
- map 함수를 실행 할 때 원본의 요소를 수정하지 말것
- filter, map에서 return 되는 값을 수정 할것 ( return 하는 것은 새로운 배열에 들어가는 요소니까 )