useReducer에 대해 이론적인 부분만 공부하고 바로 리팩터링을 도전했더니 예상치 못한 이슈가 많이 발생했다.
1. 상태 업데이트 안됨
const simpleSymbolsReducer = (state, action) => {
switch (action.type) {
case "INIT":
return action.payload;
case "ADD":
return state.map((symbol, i) => {
if (i === action.idx) {
return new Set([...symbol, action.symbol]);
} else {
return symbol;
}
});
case "REMOVE":
return state.map((symbol, i) => {
if (i === action.idx) {
return new Set();
} else {
return symbol;
}
});
default:
throw new Error("Unhandled action");
}
};
저번 글에서 작성했던 리듀서다. 문제는 ===였다.
dispatch({ type: "ADD", symbol: cursor, idx: selectedWordIdx });
이렇게 선택된 단어의 인덱스를 넘겨줬는데 idx가 string형으로 넘어갔던 것이다. 우선 임시방편으로 ===를 ==로 변경해 자료형은 무시하고 값만 비교하도록 수정하니 잘 작동했다.
내가 올해 기필코 타입스크립트를 공부 할 것이다...
2. forEach로 렌더링 시도
기존에
enterSymbol = [true, false, ..., false]
...
{enterSymbol[i] && (<img src={symbolIcons[4].src} alt="enter" />)}
위와 같이 각각 렌더링 하던 것을 하나로 묶어
simpleSymbols = [
{"PAUSE"},
{"ENTER"},
{},
{"MOUSE", "SLASH"},
... ,
{}
]
이렇게 관리하기로 했으니, 각 단어를 렌더링 할 때 해당 인덱스에 포함된 기호들을 렌더링 해 주어야 할 것이다. 그런데
simpleSymbols[i].forEach((symbol) => (
return (
<img ~~/>
);
)
이렇게 작성해도 화면에는 아무 것도 출력되지 않는다.
그 이유는 forEach가 값을 반환하지 않기 때문이다. simpleSymbols[i]에 저장된 데이터는 Set이라 .map()을 사용할 수도 없다. 따라서 for...of 구문으로 set의 값들을 순회했다.
(() => {
for (let symbol of simpleSymbols[i]) {
return (
<img
src={symbolIcons[symbol]}
alt={symbol}
key={symbol}
/>
);
}
})()
jsx 중괄호 내에서 for문을 사용할 수 없기 때문에 즉시 실행 함수로 작성했다.
3. JSON.stringify()
가장 황당한 문제였는데, JSON.stringfy()가 Set을 처리해주지 않는다는 것을 몰라서 발생한 문제였다.
사용자가 기호를 추가하면 다음과 같은 코드로 서버에 patch를 해준다.
const patchUserSymbol = useCallback(
async (simpleSymbols, highlighted, edited) => {
if (!isDone) return;
try {
const symbolObj = {
simpleSymbols: simpleSymbols,
highlight: highlighted,
edit: edited,
};
const res = await api.patch(
`/presentations/${presentation_id}/speeches/${speech_id}`,
{
params: {
"presentation-id": presentation_id,
"speech-id": speech_id,
},
userSymbol: JSON.stringify(symbolObj),
}
);
// console.log("patch user symbol response:", res);
} catch (err) {
console.log("🩸patch user symbol error:", err);
}
},
[isDone, presentation_id, speech_id]
);
그런데 sumbolObj가 제대로 생성되었음을 확인 했는데도, 서버에 전송될 때는 simpleSymbols가 모두 빈 set의 배열로 날아가는 것이다.
reducer로 묶은 simpleSymbols만 문제가 발생했기 때문에 당연히 상태 업데이트가 제대로 되지 않는 줄 알고 코드를 한참을 들여다봤는데, 찾아보니 JSON.stringfy()가 set을 모두 {}로 바꿔버리는게 문제였다.
JSON stringify a Set
How would one JSON.stringify() a Set? Things that did not work in Chromium 43: var s = new Set(['foo', 'bar']); JSON.stringify(s); // -> "{}" JSON.stringify(s.values()); // -> "{}" JSON.
stackoverflow.com
JSON.stringify 만들어보기
Table of Contents JSON이 지원하는 타입 JSON 무려 공식 홈페이지가 존재하는데, 여기서 어떤 데이터 타입을 지원하는지 나와있다. JSON은 우리가 매일 쓰고 또 그다지 어렵지 않기 때문에 그렇게 복잡
yceffort.kr
이 문제를 해결하려면 patch를 할 때마다 set을 array로 바꿔주든가 해야 하는데...
다른 이유들까지 고려해서 기호를 저장하는 형식을 조금 바꾸기로 했다. 이건 다음 글에서 자세히 써야겠다.
'React' 카테고리의 다른 글
useReducer로 useState 리팩터링 하기 (3) (0) | 2023.09.15 |
---|---|
useReducer로 useState 리팩터링 하기 (1) (0) | 2023.09.15 |
useReducer (0) | 2023.09.12 |
useEffect로 state 변경 바로 감지하기 (4) | 2023.08.08 |
State 작동 방식 (0) | 2023.08.08 |