24.04.05 21:25 작성
·
209
0
추천탭에서 게시글을 작성하면 새로고침 시 사라져버리고
추천탭에서 게시한 게시글이 "팔로우 중" 탭에서 존재하여
좋아요 버튼을 클릭하면 새로고침 시 다시 추천탭에도 해당 게시글이 나타납니다.
도무지 뭐가문제인지 모르겠네요ㅠㅠ
"use client"
import { MouseEventHandler } from 'react';
import style from './post.module.css';
import cx from 'classnames';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { Post } from '@/model/Post';
type Props = {
white? : boolean;
postId: number;
}
export default function ActionButtons({white, postId}: Props) {
const queryClient = useQueryClient();
const commented = true;
const reposted = true;
const liked = false;
const heart = useMutation({
mutationFn: () => {
return fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/posts/${postId}/heart`, {
method: 'post',
credentials: 'include',
})
},
// 클릭한 하트 상태를 실시간으로 true로 만들어줌
// post에서 검색결과,추천,팔로잉,답글 등등 쿼리키가 다양한대 어떤 쿼리키인지 무슨상황인지 알 수가 없다.
// 그래서 전부 다 해줘야 한다.
onMutate() {
const queryCache = queryClient.getQueryCache();
const queryKeys = queryCache.getAll().map(cache => cache.queryKey);
console.log('queryKeys',queryKeys);
queryKeys.forEach((querykey) => {
if(querykey[0] === 'posts') {
const value:Post | Post[] | undefined = queryClient.getQueryData(querykey); // 게시글
console.log(value)
// 싱글포스트 일 수도 있기때문에 조건문 걸어줌.
if(Array.isArray(value)){
const index = value.findIndex((v) => postId == v.postId);
// 찾고자 하는 게시글이 있는지 확인
if(index > -1) {
const shallow = [...value];
shallow[index] = {
...shallow[index],
}
// 옅은 복사해준것을 쿼리에 전송
queryClient.setQueryData(querykey, shallow);
}
}else {
// 싱글 포스트인 경우
}
}
});
},
onError() {
},
onSettled() {
}
})
// 댓글
const onClickComment = () => {}
// 리트윗
const onClickRepost = () => {
}
// 좋아요
const onClickHeart:MouseEventHandler<HTMLButtonElement> = (e) => {
e.stopPropagation();
if(liked){
// unheart.mutate();
}else{
heart.mutate();
}
}
return (
<div className={style.actionButtons}>
<div className={cx(style.commentButton, { [style.commented]: commented }, white && style.white)}>
<button onClick={onClickComment}>
<svg width={24} viewBox="0 0 24 24" aria-hidden="true">
<g>
<path
d="M1.751 10c0-4.42 3.584-8 8.005-8h4.366c4.49 0 8.129 3.64 8.129 8.13 0 2.96-1.607 5.68-4.196 7.11l-8.054 4.46v-3.69h-.067c-4.49.1-8.183-3.51-8.183-8.01zm8.005-6c-3.317 0-6.005 2.69-6.005 6 0 3.37 2.77 6.08 6.138 6.01l.351-.01h1.761v2.3l5.087-2.81c1.951-1.08 3.163-3.13 3.163-5.36 0-3.39-2.744-6.13-6.129-6.13H9.756z"></path>
</g>
</svg>
</button>
<div className={style.count}>{1 || ''}</div>
</div>
<div className={cx(style.repostButton, reposted && style.reposted, white && style.white)}>
<button onClick={onClickRepost}>
<svg width={24} viewBox="0 0 24 24" aria-hidden="true">
<g>
<path
d="M4.5 3.88l4.432 4.14-1.364 1.46L5.5 7.55V16c0 1.1.896 2 2 2H13v2H7.5c-2.209 0-4-1.79-4-4V7.55L1.432 9.48.068 8.02 4.5 3.88zM16.5 6H11V4h5.5c2.209 0 4 1.79 4 4v8.45l2.068-1.93 1.364 1.46-4.432 4.14-4.432-4.14 1.364-1.46 2.068 1.93V8c0-1.1-.896-2-2-2z"></path>
</g>
</svg>
</button>
<div className={style.count}>{1 || ''}</div>
</div>
<div className={cx([style.heartButton, liked && style.liked, white && style.white])}>
<button onClick={onClickHeart}>
<svg width={24} viewBox="0 0 24 24" aria-hidden="true">
<g>
<path
d="M16.697 5.5c-1.222-.06-2.679.51-3.89 2.16l-.805 1.09-.806-1.09C9.984 6.01 8.526 5.44 7.304 5.5c-1.243.07-2.349.78-2.91 1.91-.552 1.12-.633 2.78.479 4.82 1.074 1.97 3.257 4.27 7.129 6.61 3.87-2.34 6.052-4.64 7.126-6.61 1.111-2.04 1.03-3.7.477-4.82-.561-1.13-1.666-1.84-2.908-1.91zm4.187 7.69c-1.351 2.48-4.001 5.12-8.379 7.67l-.503.3-.504-.3c-4.379-2.55-7.029-5.19-8.382-7.67-1.36-2.5-1.41-4.86-.514-6.67.887-1.79 2.647-2.91 4.601-3.01 1.651-.09 3.368.56 4.798 2.01 1.429-1.45 3.146-2.1 4.796-2.01 1.954.1 3.714 1.22 4.601 3.01.896 1.81.846 4.17-.514 6.67z"></path>
</g>
</svg>
</button>
<div className={style.count}>{0 || ''}</div>
</div>
</div>
)
}
답변 3
0
2024. 04. 05. 22:14
아, 지금 생각하니 그 동작이 맞습니다. 트위터 따라한 건데요. 원래 게시글 작성하면 추천 탭에 뜨는데, 새로고침하면 사라집니다. 그 이유가 추천 탭은 인기글 목록이 뜨는 거라 자기가 방금 작성한 글은 인기글이 아니라 사라지는 겁니다. 팔로우 탭은 자기 게시글 포함해서 최신으로 보여주는거라 새로고침 후에도 거기에 있는거고요.
2024. 04. 05. 22:20
아하 그런것이였군요 학업하고 병행하다보니 연속해서 강의를 들을시간이 없어서 저도 깜빡하고 있었나봅니다. ㅋㅋㅋㅋ 오늘도 정성스러운답변 너무감사합니다 좋은하루보내세요~~~!
0
2024. 04. 05. 21:32
게시글 작성 코드부터 봐야겠습니다. 혹시 리액트쿼리 데브툴 설치하셨나요? 거기서 게시글 작성 시 추천글 리스트 쿼리 데이터 업데이트되는지 확인해보셔야 합니다.
2024. 04. 05. 22:12
"use client"
import {ChangeEventHandler, FormEvent, FormEventHandler, useRef, useState} from "react";
import style from './postForm.module.css';
import {Session} from "@auth/core/types";
import TextareaAutosize from 'react-textarea-autosize';
import {useMutation, useQueryClient} from "@tanstack/react-query";
import {Post} from "@/model/Post";
type Props = {
me: Session | null
}
export default function PostForm({ me }: Props) {
const imageRef = useRef<HTMLInputElement>(null);
const [preview, setPreview] = useState<Array<{ dataUrl: string, file: File } | null>>([]);
const [content, setContent] = useState('');
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: async (e: FormEvent) => {
e.preventDefault();
const formData = new FormData();
formData.append('content', content);
preview.forEach((p) => {
p && formData.append('images', p.file);
})
return fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/posts`, {
method: 'post',
credentials: 'include',
body: formData,
});
},
async onSuccess(response, variable) {
const newPost = await response.json();
setContent('');
setPreview([]);
if (queryClient.getQueryData(['posts', 'recommends'])) {
queryClient.setQueryData(['posts', 'recommends'], (prevData: { pages: Post[][] }) => {
const shallow = {
...prevData,
pages: [...prevData.pages],
};
shallow.pages[0] = [...shallow.pages[0]];
shallow.pages[0].unshift(newPost);
return shallow;
});
}
if (queryClient.getQueryData(['posts', 'followings'])) {
queryClient.setQueryData(['posts', 'followings'], (prevData: { pages: Post[][] }) => {
const shallow = {
...prevData,
pages: [...prevData.pages],
};
shallow.pages[0] = [...shallow.pages[0]];
shallow.pages[0].unshift(newPost);
return shallow;
})
}
},
onError(error) {
console.error(error);
alert('업로드 중 에러가 발생했습니다.');
}
})
const onChange: ChangeEventHandler<HTMLTextAreaElement> = (e) => {
setContent(e.target.value);
}
const onClickButton = () => {
imageRef.current?.click();
}
const onRemoveImage = (index: number) => () => {
setPreview((prevPreview) => {
const prev = [...prevPreview];
prev[index] = null;
return prev;
})
};
const onUpload: ChangeEventHandler<HTMLInputElement> = (e) => {
e.preventDefault();
if (e.target.files) {
Array.from(e.target.files).forEach((file, index) => {
const reader = new FileReader();
reader.onloadend = () => {
setPreview((prevPreview) => {
const prev = [...prevPreview];
prev[index] = {
dataUrl: reader.result as string,
file,
};
return prev;
})
};
reader.readAsDataURL(file);
});
}
};
return (
<form className={style.postForm} onSubmit={mutation.mutate}>
<div className={style.postUserSection}>
<div className={style.postUserImage}>
<img src={me?.user?.image as string} alt={me?.user?.email as string} />
</div>
</div>
<div className={style.postInputSection}>
<TextareaAutosize value={content} onChange={onChange} placeholder="무슨 일이 일어나고 있나요?"/>
<div style={{ display: 'flex' }}>
{preview.map((v, index) => (
v && (<div key={index} style={{ flex: 1 }} onClick={onRemoveImage(index)}>
<img src={v.dataUrl} alt="미리보기" style={{ width: '100%', objectFit: 'contain', maxHeight: 100 }} />
</div>)
))}
</div>
<div className={style.postButtonSection}>
<div className={style.footerButtons}>
<div className={style.footerButtonLeft}>
<input type="file" name="imageFiles" multiple hidden ref={imageRef} onChange={onUpload} />
<button className={style.uploadButton} type="button" onClick={onClickButton}>
<svg width={24} viewBox="0 0 24 24" aria-hidden="true">
<g>
<path
d="M3 5.5C3 4.119 4.119 3 5.5 3h13C19.881 3 21 4.119 21 5.5v13c0 1.381-1.119 2.5-2.5 2.5h-13C4.119 21 3 19.881 3 18.5v-13zM5.5 5c-.276 0-.5.224-.5.5v9.086l3-3 3 3 5-5 3 3V5.5c0-.276-.224-.5-.5-.5h-13zM19 15.414l-3-3-5 5-3-3-3 3V18.5c0 .276.224.5.5.5h13c.276 0 .5-.224.5-.5v-3.086zM9.75 7C8.784 7 8 7.784 8 8.75s.784 1.75 1.75 1.75 1.75-.784 1.75-1.75S10.716 7 9.75 7z"></path>
</g>
</svg>
</button>
</div>
<button className={style.actionButton} disabled={!content}>게시하기</button>
</div>
</div>
</div>
</form>
)
}
위 코드는 PostForm.tsx 코드입니다.그리고 데이터를 살펴봐도 생성 시 에는 recommends에 게시글이 있는데 새로고침하니 귀신같이 사라지네요
0
안녕하세요, 인프런 AI 인턴입니다.
Jinhye Kim님의 질문에 대해 직접적으로 일치하는 예시는 찾을 수 없었습니다. 하지만, 여러분이 겪고 계신 문제와 비슷한 상황을 다룬 예시들을 기반으로 가능한 원인과 해결 방향에 대해 안내드리겠습니다.
React Query의 useMutation 사용법:
useMutation
은 서버로 데이터를 보내는 (예: 생성, 업데이트, 삭제 등) 사이드 이펙트를 수행할 때 사용됩니다. 여러분의 코드에서는 useMutation
을 사용하여 게시글에 좋아요를 표시하는 API를 호출하고 있습니다. 이 과정에서 실제로 데이터가 올바르게 업데이트되고 있는지, 그리고 백엔드에서 적절한 응답을 보내고 있는지 확인이 필요합니다.쿼리 캐시와 상태 업데이트:
onMutate
함수 내에서 여러분은 모든 쿼리 키를 가져와서 특정 조건('posts'
)에 맞는 쿼리 데이터를 직접 수정하고 있습니다. 이 과정에서 각 쿼리 키에 해당하는 데이터가 올바르게 업데이트 되고 있는지, 그리고 해당 데이터가 UI에 즉시 반영되도록 queryClient.setQueryData
를 이용해 업데이트하고 있는지 점검해야 합니다.환경 변수 관리:
mutationFn
에서 ${process.env.NEXT_PUBLIC_BASE_URL}/api/posts/${postId}/heart
API 주소를 사용하여 요청을 보내고 있습니다. 이 때, .env
파일이나 환경 설정에 NEXT_PUBLIC_BASE_URL
이 올바르게 설정되어 있고, 이 URL이 실제로 API 서버를 가리키고 있는지 확인해 주세요.서버와의 통신 확인:
mutationFn
으로 데이터를 보내고 받는 과정에서 네트워크 요청이 성공적으로 이루어지고 있는지, 개발자 도구의 Network 탭을 통해 검토해 보세요. 네트워크 요청이 실패하거나 예상치 못한 응답을 받고 있다면, 문제 해결의 실마리를 제공할 수 있습니다.이와 관련된 더 자세한 도움이 필요할 경우, React Query의 공식 문서를 참고하거나, 구체적인 오류 메시지나 행동을 포함하여 질문을 구체화해 주시면 더 도움을 드리기 쉬울 것 같습니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
2024. 04. 05. 22:15
좋아요 누르면 다시 나온다는 건, 아직 다른 게시글들 좋아요 수가 부족해서 좋아요 1개로도 추천글 최상단으로 올라오나봅니다.