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:
parent
46b45872c1
commit
458027934a
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue