fix: add optimistic updates for like/favorite actions

- Immediately update store state on user action (no delay)
- Increment/decrement count optimistically for instant feedback
- Rollback on API error
- Use server-returned count for final accuracy
- Remove local useState, only use store for state
This commit is contained in:
imeepos 2026-01-28 20:12:40 +08:00
parent 46b45872c1
commit 458027934a
2 changed files with 49 additions and 28 deletions

View File

@ -17,7 +17,6 @@ export const useTemplateFavorite = (
templateId?: string, templateId?: string,
initialFavorited = false, initialFavorited = false,
) => { ) => {
const [favorited, setFavorited] = useState<boolean>(initialFavorited)
const [loading, setLoading] = useState<boolean>(false) const [loading, setLoading] = useState<boolean>(false)
const [error, setError] = useState<ApiError | null>(null) const [error, setError] = useState<ApiError | null>(null)
@ -29,6 +28,11 @@ export const useTemplateFavorite = (
setLoading(true) setLoading(true)
setError(null) setError(null)
// 乐观更新:立即更新状态
const currentCount = templateSocialStore.getFavoriteCount(templateId) ?? 0
templateSocialStore.setFavorited(templateId, true)
templateSocialStore.setFavoriteCount(templateId, currentCount + 1)
const controller = root.get(TemplateSocialController) const controller = root.get(TemplateSocialController)
const params: FavoriteTemplateInput = { const params: FavoriteTemplateInput = {
templateId, templateId,
@ -38,21 +42,21 @@ export const useTemplateFavorite = (
async () => await controller.favorite(params), async () => await controller.favorite(params),
) )
setLoading(false)
if (error) { if (error) {
// 回滚乐观更新
templateSocialStore.setFavorited(templateId, false)
templateSocialStore.setFavoriteCount(templateId, currentCount)
setError(error) setError(error)
setLoading(false)
return { data: null, error } return { data: null, error }
} }
setFavorited(true) // 使用服务器返回的准确数量
// 同步更新 store
templateSocialStore.setFavorited(templateId, true)
if (data?.favoriteCount !== undefined) { if (data?.favoriteCount !== undefined) {
templateSocialStore.setFavoriteCount(templateId, data.favoriteCount) templateSocialStore.setFavoriteCount(templateId, data.favoriteCount)
} }
setLoading(false)
return { data, error: null } return { data, error: null }
}, [templateId]) }, [templateId])
@ -64,6 +68,11 @@ export const useTemplateFavorite = (
setLoading(true) setLoading(true)
setError(null) setError(null)
// 乐观更新:立即更新状态
const currentCount = templateSocialStore.getFavoriteCount(templateId) ?? 0
templateSocialStore.setFavorited(templateId, false)
templateSocialStore.setFavoriteCount(templateId, Math.max(0, currentCount - 1))
const controller = root.get(TemplateSocialController) const controller = root.get(TemplateSocialController)
const params: UnfavoriteTemplateInput = { const params: UnfavoriteTemplateInput = {
templateId, templateId,
@ -73,21 +82,21 @@ export const useTemplateFavorite = (
async () => await controller.unfavorite(params), async () => await controller.unfavorite(params),
) )
setLoading(false)
if (error) { if (error) {
// 回滚乐观更新
templateSocialStore.setFavorited(templateId, true)
templateSocialStore.setFavoriteCount(templateId, currentCount)
setError(error) setError(error)
setLoading(false)
return { data: null, error } return { data: null, error }
} }
setFavorited(false) // 使用服务器返回的准确数量
// 同步更新 store
templateSocialStore.setFavorited(templateId, false)
if (data?.favoriteCount !== undefined) { if (data?.favoriteCount !== undefined) {
templateSocialStore.setFavoriteCount(templateId, data.favoriteCount) templateSocialStore.setFavoriteCount(templateId, data.favoriteCount)
} }
setLoading(false)
return { data, error: null } return { data, error: null }
}, [templateId]) }, [templateId])
@ -108,19 +117,21 @@ export const useTemplateFavorite = (
async () => await controller.checkFavorited(params), async () => await controller.checkFavorited(params),
) )
setLoading(false)
if (error) { if (error) {
setError(error) setError(error)
setLoading(false)
return { data: null, error } return { data: null, error }
} }
setFavorited(data.favorited) if (data?.favorited !== undefined) {
setLoading(false) templateSocialStore.setFavorited(templateId, data.favorited)
}
return { data, error: null } return { data, error: null }
}, [templateId]) }, [templateId])
return { return {
favorited,
loading, loading,
error, error,
favorite, favorite,

View File

@ -7,7 +7,6 @@ import { handleError } from './use-error'
import { templateSocialStore } from '@/stores/templateSocialStore' import { templateSocialStore } from '@/stores/templateSocialStore'
export const useTemplateLike = (templateId?: string) => { export const useTemplateLike = (templateId?: string) => {
const [liked, setLiked] = useState<boolean>(false)
const [loading, setLoading] = useState<boolean>(false) const [loading, setLoading] = useState<boolean>(false)
const [error, setError] = useState<ApiError | null>(null) const [error, setError] = useState<ApiError | null>(null)
@ -19,6 +18,11 @@ export const useTemplateLike = (templateId?: string) => {
setLoading(true) setLoading(true)
setError(null) setError(null)
// 乐观更新:立即更新状态
const currentCount = templateSocialStore.getLikeCount(templateId) ?? 0
templateSocialStore.setLiked(templateId, true)
templateSocialStore.setLikeCount(templateId, currentCount + 1)
const social = root.get(TemplateSocialController) const social = root.get(TemplateSocialController)
const { data, error } = await handleError( const { data, error } = await handleError(
async () => await social.like({ templateId }) async () => await social.like({ templateId })
@ -27,14 +31,14 @@ export const useTemplateLike = (templateId?: string) => {
setLoading(false) setLoading(false)
if (error) { if (error) {
// 回滚乐观更新
templateSocialStore.setLiked(templateId, false)
templateSocialStore.setLikeCount(templateId, currentCount)
setError(error) setError(error)
return { error } return { error }
} }
setLiked(true) // 使用服务器返回的准确数量
// 同步更新 store
templateSocialStore.setLiked(templateId, true)
if (data?.likeCount !== undefined) { if (data?.likeCount !== undefined) {
templateSocialStore.setLikeCount(templateId, data.likeCount) templateSocialStore.setLikeCount(templateId, data.likeCount)
} }
@ -50,6 +54,11 @@ export const useTemplateLike = (templateId?: string) => {
setLoading(true) setLoading(true)
setError(null) setError(null)
// 乐观更新:立即更新状态
const currentCount = templateSocialStore.getLikeCount(templateId) ?? 0
templateSocialStore.setLiked(templateId, false)
templateSocialStore.setLikeCount(templateId, Math.max(0, currentCount - 1))
const social = root.get(TemplateSocialController) const social = root.get(TemplateSocialController)
const { data, error } = await handleError( const { data, error } = await handleError(
async () => await social.unlike({ templateId }) async () => await social.unlike({ templateId })
@ -58,14 +67,14 @@ export const useTemplateLike = (templateId?: string) => {
setLoading(false) setLoading(false)
if (error) { if (error) {
// 回滚乐观更新
templateSocialStore.setLiked(templateId, true)
templateSocialStore.setLikeCount(templateId, currentCount)
setError(error) setError(error)
return { error } return { error }
} }
setLiked(false) // 使用服务器返回的准确数量
// 同步更新 store
templateSocialStore.setLiked(templateId, false)
if (data?.likeCount !== undefined) { if (data?.likeCount !== undefined) {
templateSocialStore.setLikeCount(templateId, data.likeCount) templateSocialStore.setLikeCount(templateId, data.likeCount)
} }
@ -93,12 +102,13 @@ export const useTemplateLike = (templateId?: string) => {
return { error } return { error }
} }
setLiked(data?.liked || false) if (data?.liked !== undefined) {
templateSocialStore.setLiked(templateId, data.liked)
}
return {} return {}
}, [templateId]) }, [templateId])
return { return {
liked,
loading, loading,
error, error,
like, like,