0. 버그 현황
- 공유하기 버튼을 누르면 내가 한 일들만 필터링이 돼서 목록이 뜬다. 이 목록들을 친구에게 카톡으로 공유할 수 있다.
- 하지만, 뒤로가기 버튼을 누르고 안 한 일들 나머지를 다시 체크하고 공유버튼을 눌렀더니 상태가 그대로 되어있다..
- 상태값이 바로바로 변경되어서 렌더링이 되지 않고있다.
1. 문제 코드
- todo 컴포넌트에서 todoItem 을 리코일을 이용해서 전역상태로 관리했다.
- todoItem 빈 배열에서 get API 를 불러와서 setTodoItem 으로 상태를 업데이트 해주었다.
function Todo() {
const [todoItem, setTodoItem] = useRecoilState<TodoItem[]>(todoItemState);
const [isLoading, setIsLoading] = useState(true);
const setToken = useSetRecoilState(tokenState);
const navigate = useNavigate();
const getTodo = async () => {
try {
const todoRes = await customAuthAxios.get("todos");
if (todoRes) {
setTodoItem(todoRes.data);
setIsLoading(false);
}
} catch (error) {
console.log(error);
}
};
useEffect(() => {
getTodo();
}, []);
if (isLoading) {
return <Loading />;
} else {
return (
<div className="bg-main_skyblue flex flex-col justify-center items-center h-screen">
<aside className="w-98 text-right mr-5 mb-5">
<KakaoShare />
</aside>
<section className="bg-main_bg_cloud max-w-7xl w-98 rounded-xl h-600 relative">
<div className="sticky top-0 pb-5 rounded-t-xl bg-main_bg_cloud ">
<h1
className="font-mono pl-10 pt-9 text-3xl font-semibold cursor-pointer"
onClick={handleRefresh}
>
Today
</h1>
<p className="font-mono pl-10 pt-3 text-sm">
What are you working on today?
</p>
</div>
<ul className="h-fit max-h-450 pt-5 pb-5 pr-10 pl-10 grid grid-cols-2 gap-4 overflow-y-scroll">
{todoItem.map((postIt) => {
return (
<PostItem
key={postIt.id}
todoId={postIt.id}
todoList={todoItem}
setTodoList={setTodoItem}
isCompleted={postIt.isCompleted}
>
{postIt.todo}
</PostItem>
);
})}
</ul>
</section>
</div>
);
}
}
export default Todo;
// atoms.ts
export const todoItemState = atom<TodoItem[]>({
key: "todoItemState",
default: [],
effects_UNSTABLE: [persistAtom],
});
- Todo 컴포넌트에서 공유하기 버튼을 Result 컴포넌트 페이지로 이동하는데, 마찬가지로 리코일에서 todoItem 을 불러와서 이 todoItem 에서 filter 를 적용해서 보여주는 것이다. 헌데, handleMovePreviousPage 이벤트 핸들러가 발동되어 이전 페이지에 가서 다시 isCompleted 를 false 를 하고 다시 공유버튼을 눌렀을 경우, completedTodos 에서 filter 가 제대로 되지 않고 있었다.
function Result() {
const navigate = useNavigate();
const [todoItem] = useRecoilState<TodoItem[]>(todoItemState);
const completedTodos = todoItem.filter((item) => {
return item.isCompleted === true;
});
const handleMovePreviousPage = () => {
navigate(-1);
};
return (
<div className="bg-main_skyblue flex flex-col justify-center items-center h-screen">
<section className="bg-main_bg_cloud max-w-7xl w-98 rounded-xl h-600 relative">
<div className="sticky top-0 pb-5 rounded-t-xl bg-main_bg_cloud ">
<h1 className=" text-center pt-9 text-3xl font-semibold ">
오늘 한 일 목록 공유하기
</h1>
</div>
<ul className="h-fit max-h-450 pt-11 pb-5 pr-10 pl-10 grid grid-cols-2 gap-4 overflow-y-scroll">
{completedTodos.map((postIt) => {
return (
<ResultPostItem key={postIt.id} timeTypes={postIt.todo.slice(-1)}>
{postIt.todo.slice(0, -1)}
</ResultPostItem>
);
})}
</ul>
</section>
<div className="flex flex-col gap-y-3 mt-5">
<Button size="large" onClick={handleShareKaKao}>
카카오톡으로 공유하기
</Button>
<Button size="large" onClick={handleMovePreviousPage}>
뒤로가기
</Button>
</div>
</div>
);
}
2. 해결 방법
- Result 컴포넌트에서 console.log(todoItem) 을 해서 살펴보았다.
- 한개만 true 로 만들었다.
- 다시 뒤로가기 버튼을 누른 후 모든 todoItem 의 isCompleted를 true 로 변경하였다.
useRecoilState를 사용해서 상태를 가지고 오고있는데 Result 컴포넌트가 리코일 상태 변경을 바로바로 감지하지 못하고 있었다.
이유는 각각의 todoItem은 get API 를 불러와야지 setTodoItem 이 발동되어서 상태가 변경이 된다.
Result 컴포넌트에서 뒤로가기를 누르고 Todo 컴포넌트 페이지에서 다시 isCompleted 상태 변경을 하고 handleMoveToResultPage 이벤트 핸들러가 발동하면 get API 가 불러와지지 않았기 때문에 상태 변경이 되지않는다.
get API 를 불러올 때 상태 변경이 되는게 아니라 isCompleted 만 바꿨을 때도 상태 변경이 되게 해야한다.
isCompleted 는 postItem 컴포넌트에서 변경하고 있다.
handleTodoCompleted 함수가 실행이 될 때 todoItem 전역 상태의 isCompleted 도 상태 변경이 되게 하였다.
function PostItem({
children,
todoId,
todoList,
setTodoList,
isCompleted,
}: PostItemProps) {
const len = children.length;
const timeType = children.slice(-1);
const content = children.slice(0, len - 1);
const [, setTodoItem] = useRecoilState(todoItemState);
const [updateToggle, setUpdateToggle] = useState(false);
const [updatedContent, setUpdatedContent] = useState(content);
const [isCompletedTodo, setIsCompletedTodo] = useState(isCompleted);
const todoInput = useRef<HTMLTextAreaElement>(null);
const handleTodoDelete = () => {
const deleteConfirm = confirm("정말로 삭제하시겠습니까?");
if (deleteConfirm) {
const deleteTodo = async () => {
try {
await customAuthAxios.delete(`todos/${todoId}`);
setTodoList(
[...todoList].filter((todoItem) => todoItem.id !== todoId)
);
} catch (error) {
console.log(error);
}
};
deleteTodo();
}
};
const handleTodoUpdate = async () => {
if (updateToggle) {
const updateData = {
todo: updatedContent + timeType,
isCompleted: isCompletedTodo,
};
try {
await customAuthAxios.put(`todos/${todoId}`, updateData);
} catch (error) {
console.log(error);
}
}
setUpdateToggle((prev) => !prev);
};
const handleTodoCompleted = async () => {
const updateData = {
todo: updatedContent + timeType,
isCompleted: !isCompleted,
};
try {
await customAuthAxios.put(`todos/${todoId}`, updateData);
setIsCompletedTodo((prev) => !prev);
setTodoItem((prevTodos) =>
prevTodos.map((todo) =>
todo.id === todoId ? { ...todo, isCompleted: !isCompleted } : todo
)
);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
todoInput.current?.focus();
}, [updateToggle]);
return (
<li className="font-semibold tracking-widest relative h-fit">
<div
className={`${
timeType === "1"
? "bg-post_red"
: timeType === "2"
? "bg-post_yellow"
: "bg-post_blue"
} w-50 h-32 p-1 shadow shadow-black break-all`}
>
{!updateToggle ? (
<p
className={`${isCompletedTodo ? "line-through" : ""} h-24`}
onClick={handleTodoCompleted}
>
{updatedContent}
</p>
) : (
<textarea
ref={todoInput}
className="w-32 h-24 bg-transparent tracking-widest resize-none"
value={updatedContent}
onChange={(e) => {
setUpdatedContent(e.target.value);
}}
maxLength={30}
/>
)}
<button
onClick={handleTodoDelete}
className="absolute right-2 bottom-1"
>
<FontAwesomeIcon
icon={faTrashCan}
className="cursor-pointer hover:opacity-80"
opacity={0.2}
/>
</button>
<button onClick={handleTodoUpdate} className="absolute left-2 bottom-1">
<FontAwesomeIcon
icon={!updateToggle ? faPenToSquare : faCheck}
className="cursor-pointer hover:opacity-80"
opacity={0.2}
/>
</button>
</div>
</li>
);
}
export default PostItem;
'프로젝트 이모저모' 카테고리의 다른 글
[투두리스트 페어프로그래밍] recoil 새로 고침 시 전역 데이터 초기화 (0) | 2023.07.24 |
---|---|
[투두리스트 페어프로그래밍 버그] 로그아웃 시 페이지 이동 안됨(useEffect,Context API) (0) | 2023.07.11 |
[투두리스트 페어프로그래밍 버그] Context Api - 토큰에 따른 페이지 접근 제한 (0) | 2023.07.10 |
[투두리스트 페어프로그래밍] 전역상태관리 VS get API 재호출 (0) | 2023.07.07 |
[투두리스트 페어프로그래밍] 지역변수, useRef, useState 차이 (0) | 2023.07.05 |