개발자
안녕하세요. recoil을 학습중입니다. 아무리 해도 type에러가 자꾸떠서요.. 지금 #으로 표시해둔 selector의 set부분이 에러가 뜹니다. initailState를 임의로 값을 주었구요 지금 현재 Tag[] 로 타입을 정의했습니다. selector의 newValue에 type을 선언안하니까 에러가 뜨더라구요 제 생각에는 initialState가 Tag[] 배열의 형태인데 컴포넌트에서 넘어오는 newValue의 타입은 Tag이기 때문에 에러가 발생하는거같습니다. 그래서 newValue에 타입을 지정해줬더니 에러가 set쪽으로 넘어가더라구요. 임의의 값을 주어도 이런 에러가 발생하진 않아야된다고 생각하는데, 아무리 생각해봐도 도저히 해결이 안되네요.. 도움이 필요합니다. <components> ``` const [tagsState, setTagsState] = useRecoilState(tagsListSelector); const newTag = { tag: inputText.toLocaleLowerCase(), id: v4() }; setTagsState(newTag); ``` <atoms> ``` interface Tag { tag: string; id: string; } const initialState:Tag[] = [ { tag: "AAA", id: v4() }, { tag: "BBB", id: v4() }, { tag: "CCC", id: v4() }, ]; export const tagsListState = atom({ key: "tagsListState", default: initialState, }); export const tagsListSelector = selector({ key: "tagsListSelector", get: ({ get }) => { return get(tagsListState); }, #set#: ({ set, get }, newValue: Tag) => { const currentTagsList = get(tagsListState); if (currentTagsList.find(({ tag }) => tag === newValue.tag)) { toast.warning("이미 존재하는 태그입니다."); } else { set(tagsListState, [...currentTagsList, newValue]); toast.info("새로운 태그가 등록되었습니다."); } }, }); ```
답변 1
인기 답변
코드에 보면 크게 두가지 문제가 보입니다. 우선 recoilState는 React State와 형식이 크게 다르지 않습니다. const [strArr,setStrArr] = useState<string[]>([]); 라는 State를 만들고 Set을 하는 상황을 생각해보겠습니다. const newValue:string = ''; setStrArr(newValue); 이 코드는 newValue에서 에러가 납니다. Argument of type 'string' is not assignable to parameter of type 'SetStateAction<string[]>' 해당 SetStateAction이 string타입의 배열을 아규먼트로 받고있는데 저는 string타입의 newValue를 입력하고 있어서죠 다시 작성자님이 작성해주신 코드를 한번 보겠습니다. #set#: ({ set, get }, newValue: Tag) => { const currentTagsList = get(tagsListState); if (currentTagsList.find(({ tag }) => tag === newValue.tag)) { toast.warning("이미 존재하는 태그입니다."); } else { set(tagsListState, [...currentTagsList, newValue]); toast.info("새로운 태그가 등록되었습니다."); } }, 보시면 tagsListState는 타입이 Tag타입의 배열입니다. 그런데 newValue는 Tag타입으로 입력하고있으니 타입이 맞지 않습니다. 그리고 recoil의 selector는 newValue가 항상 특정타입과 DefaultValue를 동시에 받을 수 있게 처리가 되어있어야 합니다. 아래는 selector의 set 파라미터의 타입입니다. ============================ export interface ReadWriteSelectorOptions<T> extends ReadOnlySelectorOptions<T> { set: ( opts: { set: SetRecoilState; get: GetRecoilValue; reset: ResetRecoilState; }, newValue: T | DefaultValue, ) => void; } ================================ 원하시는 동작을 수행하려면 아래와같이 작성하면 될 것 같습니다. <components> ``` const [tagsState, setTagsState] = useRecoilState(tagsListSelector); const newTag = { tag: inputText.toLocaleLowerCase(), id: v4() }; setTagsState([...tagsState,newTag]); ``` <atoms> export const tagsListSelector = selector({ key: "tagsListSelector", get: ({ get }) => { return get(tagsListState); }, set: ({ set, reset }, newValue) => { // newValue가 DefaultValue인 경우 리셋 로직을 처리 if (newValue instanceof DefaultValue) { reset(tagsListState); return; } //새로 입력받은 태그 리스트를 중복제거 const filterdNewValue = [...new Set(newValue)]; //중복을 제거했는제도 기존의 값과 길이가 같다면 새로 추가된 태그가 중복이 아니기때문에 중복인지 여부를 판단할 변수에 저장 const isNotDuplicate = filterdNewValue.length === newValue.length; // //중복 여부에 따라 다른 메시지 입력 isNotDuplicate ? toast.info("새로운 태그가 등록되었습니다.") : toast.warning("이미 존재하는 태그입니다."); set(tagsListState, filterdNewValue); }, }); 굳이 reset을 처리할 필요가 없다면 아래와 같이 간단하게 처리해줘도 됩니다. ... set: ({ set }, newValue) => { if (newValue instanceof DefaultValue) return; ...
익명
작성자
2023년 12월 05일
안녕하세요. 알기쉬운 설명과 답변 너무 감사합니다. 이해가 되었습니다. 근 본적인 부분을 고쳤어야했는데 아직 배움이 모자라네요. 혹시 질문을 더 드려도 될까요? setTagsState() 이부분에서 새로 생성된 newTag만 넘긴뒤에 selector에서 나머지 처리들을 하고싶었는데요. 해람님의 설명을 듣고나니 애초에 타입이 Tag타입의 배열이 안되기때문에 성립이 될 수 없는 방식인게 맞을까요? 그리고 tag의 값을 remove하는 기능을 만들고 싶은데요. 처음에는 tagsListSelector안에 if문으로 create, remove, edit을 나누었습니다. 하나의 selector안에 여러기능을쓰는게 맞는건지 tagsListSelector와 같은 파일안에 새로운 selector를 만들어서 ( 예시:tagsRemoveSelector) 처리하는게 더 낫겟죠?
문정동개발자
프론트엔드 • 2023년 12월 06일
해당 기능은 커스텀 훅을 통해 구현하시면 좋을 것 같습니다. ////tag_hook.tsx export function useTag() { const [tagsState, setTagsState] = useRecoilState(tagsListSelector); function createTag(tag: string) { const newTag = { tag, id: v4() }; setTagsState([...tagsState, newTag]); } function editTag(tag: string) { //수정 로직 } function deleteTag(tag: string) { //수정 로직 } return { createTag, editTag, deleteTag }; } /////component const { createTag } = useTag(); ... createTag(inputText.toLocaleLowerCase())
지금 가입하면 모든 질문의 답변을 볼 수 있어요!
현직자들의 명쾌한 답변을 얻을 수 있어요.
이미 회원이신가요?
지금 가입하면 모든 질문의 답변을 볼 수 있어요!