1) 기존 상태 관리 - useState
기존에는 노드들(텍스트, 이미지)의 상태를 useState를 활용해서 상태 관리했다.
const [nodes, setNodes] = useState([])
이미지 편집기에서 특정 노드의 속성을 변경하는 경우가 많은데, 이런 경우에 특정 노드를 id를 통해서 찾고 걔의 속성을 변경하는 식의 코드가 들어가야 한다. immer을 사용해서 객체, 배열의 상태 변경을 쉽게 해도 중복되는 코드의 양이 너무 많았다.
setNodes(state, draft => {
const target = draft.find(node => node.id === targetId)
target[property] = newProperty
})
이런 코드들이 많은 곳에 중복으로 들어갔다.
2) 변경 상태 관리 - useReducer
그래서 useReducer를 활용해서 상태관리 부분을 reducer.js로 따로 빼놓고 dispatch 해서 사용하는 식으로 변경했다.
export function arrayOfObjectReducer(state, action) {
let newState = state.slice()
let target = null
let targetIdx
let maxId
if(action.id) {
target = newState.find(each => each.id === action.id)
}
// console.log(action)
switch (action.type) {
case 'CHANGE__ITEM':
targetIdx = newState.findIndex(each => each.id===action.id)
newState[targetIdx] = {...newState[targetIdx], ...action.changes}
break
case 'ADD__ITEM':
maxId = -1
newState.forEach(item => {
if (item.id > maxId) maxId=item.id
})
newState = [...newState, {...action.item, id: state.length > 0 ? maxId+1 : 1}]
break
case 'DELETE__ITEM':
newState = newState.filter((each) => each.id !== action.id)
break
case 'COPY__ITEM':
const copiedItem = {...newState.find(each => each.id === action.id)}
copiedItem.x += 20
copiedItem.y += 20
maxId = -1
newState.forEach(item => {
if (item.id > maxId) maxId=item.id
})
copiedItem.id = state.length > 0 ? maxId+1 : 1
newState = [...newState, copiedItem]
break
case 'SET':
newState = action.newState
break
case 'MOVE__FRONT':
newState = [...newState.filter(each => each.id !== action.id), target]
break
case 'MOVE__REAR':
newState = [target, ...newState.filter(each => each.id !== action.id)]
break
case 'MOVE__FORWARD':
// 인덱스 한칸 뒤로 +1
targetIdx = newState.findIndex(each => each.id === action.id)
// 이미 맨뒤에있으면 안함
if(targetIdx === newState.length-1) break
// 서로 바꾸기
change(newState, targetIdx, targetIdx+1)
break
case 'MOVE__BACKWARD':
// 인덱스 한칸 앞으로 -1
targetIdx = newState.findIndex(each => each.id === action.id)
if(targetIdx === 0) break
change(newState, targetIdx, targetIdx-1)
break
default:
break
}
return newState
}
요런 식으로 특정 노드의 상태 변경, 삭제, 추가, Z-index 변경 등을 따로 reducer.js에 담은 다음에 dispatch해서 사용하도록 했다.
이렇게 했더니 중복되는 부분이 사라지고 상태 변경에 문제가 생겼을 때 상태관리 부분만 체크하면 되서 훨씬 디버깅하기에도 편해졌다. (기존에는 해당 로직이 있는 함수부분과 상태 2부분을 확인해야 했다.)
결론
useReducer는 사실 useState(state => reducer(state)) 와 같다.
객체의 속성을 변경하거나 객체들로 이뤄진 배열의 경우에는 useState로만 상태 관리하기에는 중복, 디버깅하기 어려운 문제가 발생한다.
이런 경우에는 useReducer를 활용해서 효과적으로 상태 관리를 할 수 있다.
'프로젝트 회고 > 이미지편집기 개발' 카테고리의 다른 글
7. 정리 - 끝 (0) | 2022.07.20 |
---|---|
6. 개발 - 뒤로가기(작업 취소), 앞으로 가기(복구) Z-index 조정 기능 (0) | 2022.07.20 |
4. 개발 - 편집기 주 화면 구현, 배경, 노드(이미지, 텍스트) 추가, 편집 기능 (0) | 2022.06.30 |
3. 개발 - 기본 구조 (0) | 2022.06.19 |
2. 액션 정의하기, 앱 구조와 화면 레이아웃 잡기 (0) | 2022.06.15 |