tagList state에서 태그가 3개 체크되게 하고, 체크된 값에 따라서 list?tagList=VEGAN,ZERO_WASTE 이런 식으로 API URL 요청을 해야 한다.
const tagsData = [
{ checked: false, value: 'VEGAN', name: '비건' },
{ checked: false, value: 'VEGANRECIPE', name: '비건 레시피' },
...
];
export default function ChallengeList() {
const urlSearchParams = new URLSearchParams(location.search);
const tagListParam = urlSearchParams.get('tagList') || '';
const [tagList, setTagList] = useState(tagsData);
...
const { data } = useQuery(
[
`${categoryParam}Challenge/${orderParam}/${pageParam}/${queryParam}/${searchTypeParam}`,
categoryParam,
queryParam,
orderParam,
pageParam,
searchTypeParam,
tagListParam,
],
() =>
getChallengeList({
query: queryParam,
page: Number(pageParam),
order: orderParam,
category: categoryParam,
searchType: searchTypeParam,
tagList: tagListParam,
})
);
// 태그들을 변경하는 부분
const handleChageTagList = (value: ChallengeTag) => {
setTagList((prev) => {
const checkable = prev.filter((p) => p.checked).length <= 2;
const newTagList = prev.map((p) => {
if (!p.checked && !checkable) return p;
return p.value === value ? { ...p, checked: !p.checked } : p;
});
const tagListStr = newTagList
.filter((tag) => tag.checked)
.map((cTag) => cTag.value)
.join(',');
changeSearchParams([['tagList', tagListStr]])();
return newTagList;
});
};
return ();
}
처음 시도한 방법이 handleChageTagList
함수 부분에서 체크된 newTagList 상태를 filter, join 등으로 "VEGAN,ETC" 문자열 형식으로 만들어 search param을 바꿨는데 에러가 났다.
Warning: Cannot update a component (
BrowserRouter
) while rendering a different component (ChallengeList
). To locate the bad setState() call insideChallengeList
, follow the stack trace as described in
ChallengeList를 렌더링하는 동안 BrowserRouter를 업데이트할 수 없습니다. ChallengeList 내부에서 잘못된 setState() 호출을 찾으려면 설명된 대로 스택 추적을 따르세요?!
구글링!
저 코드는 단순히 state만 변경하는 것이 아니라 두 가지? 작업을 하는데,, newTagList가 선택된 태그를 checked: true or false로 바꾼 새로운 tagList 데이터인데 태그가 선택돼서 [...tagList, {checked: true, value: 'ETC', name: '기타'}]
이런 식으로 바뀐 상태면 tagListStr은 'ETC' 가 되고, changeSearchParams
란 함수에서 tagList params 부분을 'ETC'로 바꿔준 후 newTagList를 return 해서 tagList state를 변경한다.
setState에서 이전 state 정보를 가지고 변경하면서 url 도 바꿔줘야 하는데 저 함수 밖에서 어떻게 tagList state가 바뀔 때마다 params를 바꿀 수 있을까..? useEffect 아직도 잘 모르겠지만 state가 바뀔 때 사용하는 것은 별로 추천하지 않는다는 글이 생각나서ㅜㅜ
상태를 바꿈과 동시에 router도 변경하려면 useEffect 말곤 방법이 떠오르지 않는다. 그래서 useState가 아니라 그냥 searchParam으로만 관리할 수 있게 작성해 봐야겟다
tagListParam 변수 문자열을 split 해서 tagList 배열로 만들었다
완성코드
export default function ChallengeList() {
const tagListParam = urlSearchParams.get('tagList') || '';
const tagList = tagListParam ? tagListParam.split(',') : [];
const changeSearchParams =
(obj: { name: string; value: string } | [string, string][]) => () => {
if (Array.isArray(obj)) {
obj.forEach(([name, value]) => {
urlSearchParams.set(name, value);
});
} else {
urlSearchParams.set(obj.name, obj.value);
}
navigate(`${location.pathname}?${urlSearchParams.toString()}`);
};
const handleChageTagList = (value: ChallengeTag) => {
if (!tagList.includes(value) && tagList.length > 2) return;
if (tagList.includes(value)) {
if (tagList.length === 1) {
clearSearchParams('tagList')();
} else {
changeSearchParams({
name: 'tagList',
value: tagList.filter((tag) => tag !== value).join(','),
})();
}
} else {
changeSearchParams({
name: 'tagList',
value: [...tagList, value].join(','),
})();
}
};
return (
<div>
{openTags &&
tagsData.map((tag) => (
<button
key={tag.name}
onClick={() => handleChageTagList(tag.value as ChallengeTag)}
className={`select-none border border-gray-3 rounded-sm px-2 py-1 mr-1 mt-1 text-sm
${
tagList.includes(tag.value) ? 'bg-gray-3 text-white' : 'text-gray-4'
}
`}
>
{tag.name}
</button>
))}
</div>
);
}
실수한 부분들!
먼저 tagList searchParams가 저장된 tagListParam 변수에 아무것도 없으면 split을 했을 때 tagList 변수엔 ['']
빈 문자열이 담겨서 [...tagList, value].join(',')
을 했을 때 값이 하나라면 'VEGAN'이 아닌 ',VEGAN'으로 바뀌어 제대로 조회할 수 없었다.
그래서
const tagList = tagListParam ? tagListParam.split(',') : [];
tagListParam이 빈 문자열이면 []
아니면 split을 해서 저장했다
그리고
tagListParam은 문자열이고 tagList는 배열인데 둘 다 include 메서드를 사용할 수 있다. 근데 만약 선택된 tag의 value가 'VEGANRECIPE' 이면 tagListParam.include를 했을 때 VEGAN 값도 true라 선택됐을 때의 css가 같이 적용된다.
그래서 tagListParam을 배열로 바꾼 tagList 변수에 include를 적용해서 태그가 선택됐는지 확인했다.