fix: bug
This commit is contained in:
145
stores/templateSocialStore.example.ts
Normal file
145
stores/templateSocialStore.example.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* templateSocialStore 使用示例
|
||||
*
|
||||
* 这个文件展示了如何使用 templateSocialStore 来管理模板的点赞和收藏状态
|
||||
*/
|
||||
|
||||
import { useTemplateSocialStore } from './templateSocialStore'
|
||||
|
||||
// 示例 1: 基本使用
|
||||
export function Example1_BasicUsage() {
|
||||
const { isLiked, setLiked } = useTemplateSocialStore()
|
||||
|
||||
const handleToggleLike = (templateId: string) => {
|
||||
const currentState = isLiked(templateId)
|
||||
setLiked(templateId, !currentState)
|
||||
}
|
||||
|
||||
return { handleToggleLike }
|
||||
}
|
||||
|
||||
// 示例 2: 批量更新状态(例如从 API 获取后)
|
||||
export function Example2_BatchUpdate() {
|
||||
const { setLikedStates, setFavoritedStates } = useTemplateSocialStore()
|
||||
|
||||
// 假设从 API 获取了模板列表及其社交状态
|
||||
const updateFromAPI = (apiResponse: {
|
||||
templates: Array<{ id: string; liked: boolean; favorited: boolean }>
|
||||
}) => {
|
||||
const likedStates: Record<string, boolean> = {}
|
||||
const favoritedStates: Record<string, boolean> = {}
|
||||
|
||||
apiResponse.templates.forEach((template) => {
|
||||
likedStates[template.id] = template.liked
|
||||
favoritedStates[template.id] = template.favorited
|
||||
})
|
||||
|
||||
// 批量更新
|
||||
setLikedStates(likedStates)
|
||||
setFavoritedStates(favoritedStates)
|
||||
}
|
||||
|
||||
return { updateFromAPI }
|
||||
}
|
||||
|
||||
// 示例 3: 在 React 组件中使用
|
||||
export function Example3_ComponentUsage() {
|
||||
const templateId = 'template-123'
|
||||
|
||||
// 获取状态和方法
|
||||
const liked = useTemplateSocialStore((state) => state.isLiked(templateId))
|
||||
const favorited = useTemplateSocialStore((state) => state.isFavorited(templateId))
|
||||
const setLiked = useTemplateSocialStore((state) => state.setLiked)
|
||||
const setFavorited = useTemplateSocialStore((state) => state.setFavorited)
|
||||
|
||||
const handleLike = () => {
|
||||
setLiked(templateId, !liked)
|
||||
}
|
||||
|
||||
const handleFavorite = () => {
|
||||
setFavorited(templateId, !favorited)
|
||||
}
|
||||
|
||||
return {
|
||||
liked,
|
||||
favorited,
|
||||
handleLike,
|
||||
handleFavorite,
|
||||
}
|
||||
}
|
||||
|
||||
// 示例 4: 使用选择器优化性能
|
||||
export function Example4_SelectorOptimization() {
|
||||
const templateId = 'template-123'
|
||||
|
||||
// 只订阅需要的状态,避免不必要的重渲染
|
||||
const liked = useTemplateSocialStore((state) => state.likedMap[templateId])
|
||||
const favorited = useTemplateSocialStore((state) => state.favoritedMap[templateId])
|
||||
|
||||
return { liked, favorited }
|
||||
}
|
||||
|
||||
// 示例 5: 清空状态(例如用户登出时)
|
||||
export function Example5_ClearState() {
|
||||
const clear = useTemplateSocialStore((state) => state.clear)
|
||||
|
||||
const handleLogout = () => {
|
||||
// 清空所有社交状态
|
||||
clear()
|
||||
// ... 其他登出逻辑
|
||||
}
|
||||
|
||||
return { handleLogout }
|
||||
}
|
||||
|
||||
// 示例 6: 结合 API 调用
|
||||
export function Example6_WithAPI() {
|
||||
const { setLiked, isLiked } = useTemplateSocialStore()
|
||||
|
||||
const handleLikeWithAPI = async (templateId: string) => {
|
||||
const currentState = isLiked(templateId)
|
||||
|
||||
try {
|
||||
// 乐观更新:立即更新 UI
|
||||
setLiked(templateId, !currentState)
|
||||
|
||||
// 调用 API
|
||||
// await api.likeTemplate(templateId)
|
||||
|
||||
// 如果 API 调用失败,回滚状态
|
||||
// catch (error) {
|
||||
// setLiked(templateId, currentState)
|
||||
// throw error
|
||||
// }
|
||||
} catch (error) {
|
||||
console.error('Failed to like template:', error)
|
||||
}
|
||||
}
|
||||
|
||||
return { handleLikeWithAPI }
|
||||
}
|
||||
|
||||
// 示例 7: 获取所有点赞的模板
|
||||
export function Example7_GetAllLiked() {
|
||||
const likedMap = useTemplateSocialStore((state) => state.likedMap)
|
||||
|
||||
const getLikedTemplateIds = (): string[] => {
|
||||
return Object.keys(likedMap).filter((id) => likedMap[id])
|
||||
}
|
||||
|
||||
const likedCount = getLikedTemplateIds().length
|
||||
|
||||
return { getLikedTemplateIds, likedCount }
|
||||
}
|
||||
|
||||
// 示例 8: 重置特定模板的状态
|
||||
export function Example8_ResetTemplate() {
|
||||
const { setLiked, setFavorited } = useTemplateSocialStore()
|
||||
|
||||
const resetTemplateState = (templateId: string) => {
|
||||
setLiked(templateId, false)
|
||||
setFavorited(templateId, false)
|
||||
}
|
||||
|
||||
return { resetTemplateState }
|
||||
}
|
||||
352
stores/templateSocialStore.test.ts
Normal file
352
stores/templateSocialStore.test.ts
Normal file
@@ -0,0 +1,352 @@
|
||||
import { renderHook, act } from '@testing-library/react-native'
|
||||
import { useTemplateSocialStore } from './templateSocialStore'
|
||||
|
||||
describe('useTemplateSocialStore', () => {
|
||||
beforeEach(() => {
|
||||
// 每个测试前清空 store 状态
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
act(() => {
|
||||
result.current.clear()
|
||||
})
|
||||
})
|
||||
|
||||
describe('initial state', () => {
|
||||
it('should have empty likedMap initially', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
expect(result.current.likedMap).toEqual({})
|
||||
})
|
||||
|
||||
it('should have empty favoritedMap initially', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
expect(result.current.favoritedMap).toEqual({})
|
||||
})
|
||||
})
|
||||
|
||||
describe('setLiked', () => {
|
||||
it('should set liked status for a template', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
act(() => {
|
||||
result.current.setLiked('template-1', true)
|
||||
})
|
||||
|
||||
expect(result.current.likedMap['template-1']).toBe(true)
|
||||
})
|
||||
|
||||
it('should override existing liked status', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
act(() => {
|
||||
result.current.setLiked('template-1', true)
|
||||
})
|
||||
|
||||
act(() => {
|
||||
result.current.setLiked('template-1', false)
|
||||
})
|
||||
|
||||
expect(result.current.likedMap['template-1']).toBe(false)
|
||||
})
|
||||
|
||||
it('should not affect other templates when setting one', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
act(() => {
|
||||
result.current.setLiked('template-1', true)
|
||||
result.current.setLiked('template-2', false)
|
||||
})
|
||||
|
||||
expect(result.current.likedMap['template-1']).toBe(true)
|
||||
expect(result.current.likedMap['template-2']).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('setFavorited', () => {
|
||||
it('should set favorited status for a template', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
act(() => {
|
||||
result.current.setFavorited('template-1', true)
|
||||
})
|
||||
|
||||
expect(result.current.favoritedMap['template-1']).toBe(true)
|
||||
})
|
||||
|
||||
it('should override existing favorited status', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
act(() => {
|
||||
result.current.setFavorited('template-1', true)
|
||||
})
|
||||
|
||||
act(() => {
|
||||
result.current.setFavorited('template-1', false)
|
||||
})
|
||||
|
||||
expect(result.current.favoritedMap['template-1']).toBe(false)
|
||||
})
|
||||
|
||||
it('should not affect other templates when setting one', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
act(() => {
|
||||
result.current.setFavorited('template-1', true)
|
||||
result.current.setFavorited('template-2', false)
|
||||
})
|
||||
|
||||
expect(result.current.favoritedMap['template-1']).toBe(true)
|
||||
expect(result.current.favoritedMap['template-2']).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('setLikedStates', () => {
|
||||
it('should batch set liked states', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
act(() => {
|
||||
result.current.setLikedStates({
|
||||
'template-1': true,
|
||||
'template-2': false,
|
||||
'template-3': true,
|
||||
})
|
||||
})
|
||||
|
||||
expect(result.current.likedMap['template-1']).toBe(true)
|
||||
expect(result.current.likedMap['template-2']).toBe(false)
|
||||
expect(result.current.likedMap['template-3']).toBe(true)
|
||||
})
|
||||
|
||||
it('should merge with existing liked states', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
act(() => {
|
||||
result.current.setLiked('template-1', true)
|
||||
})
|
||||
|
||||
act(() => {
|
||||
result.current.setLikedStates({
|
||||
'template-2': true,
|
||||
'template-3': false,
|
||||
})
|
||||
})
|
||||
|
||||
expect(result.current.likedMap['template-1']).toBe(true)
|
||||
expect(result.current.likedMap['template-2']).toBe(true)
|
||||
expect(result.current.likedMap['template-3']).toBe(false)
|
||||
})
|
||||
|
||||
it('should override existing states with batch update', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
act(() => {
|
||||
result.current.setLiked('template-1', true)
|
||||
})
|
||||
|
||||
act(() => {
|
||||
result.current.setLikedStates({
|
||||
'template-1': false,
|
||||
'template-2': true,
|
||||
})
|
||||
})
|
||||
|
||||
expect(result.current.likedMap['template-1']).toBe(false)
|
||||
expect(result.current.likedMap['template-2']).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('setFavoritedStates', () => {
|
||||
it('should batch set favorited states', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
act(() => {
|
||||
result.current.setFavoritedStates({
|
||||
'template-1': true,
|
||||
'template-2': false,
|
||||
'template-3': true,
|
||||
})
|
||||
})
|
||||
|
||||
expect(result.current.favoritedMap['template-1']).toBe(true)
|
||||
expect(result.current.favoritedMap['template-2']).toBe(false)
|
||||
expect(result.current.favoritedMap['template-3']).toBe(true)
|
||||
})
|
||||
|
||||
it('should merge with existing favorited states', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
act(() => {
|
||||
result.current.setFavorited('template-1', true)
|
||||
})
|
||||
|
||||
act(() => {
|
||||
result.current.setFavoritedStates({
|
||||
'template-2': true,
|
||||
'template-3': false,
|
||||
})
|
||||
})
|
||||
|
||||
expect(result.current.favoritedMap['template-1']).toBe(true)
|
||||
expect(result.current.favoritedMap['template-2']).toBe(true)
|
||||
expect(result.current.favoritedMap['template-3']).toBe(false)
|
||||
})
|
||||
|
||||
it('should override existing states with batch update', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
act(() => {
|
||||
result.current.setFavorited('template-1', true)
|
||||
})
|
||||
|
||||
act(() => {
|
||||
result.current.setFavoritedStates({
|
||||
'template-1': false,
|
||||
'template-2': true,
|
||||
})
|
||||
})
|
||||
|
||||
expect(result.current.favoritedMap['template-1']).toBe(false)
|
||||
expect(result.current.favoritedMap['template-2']).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isLiked', () => {
|
||||
it('should return true for liked template', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
act(() => {
|
||||
result.current.setLiked('template-1', true)
|
||||
})
|
||||
|
||||
expect(result.current.isLiked('template-1')).toBe(true)
|
||||
})
|
||||
|
||||
it('should return false for unliked template', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
act(() => {
|
||||
result.current.setLiked('template-1', false)
|
||||
})
|
||||
|
||||
expect(result.current.isLiked('template-1')).toBe(false)
|
||||
})
|
||||
|
||||
it('should return false for non-existent template', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
expect(result.current.isLiked('template-1')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isFavorited', () => {
|
||||
it('should return true for favorited template', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
act(() => {
|
||||
result.current.setFavorited('template-1', true)
|
||||
})
|
||||
|
||||
expect(result.current.isFavorited('template-1')).toBe(true)
|
||||
})
|
||||
|
||||
it('should return false for unfavorited template', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
act(() => {
|
||||
result.current.setFavorited('template-1', false)
|
||||
})
|
||||
|
||||
expect(result.current.isFavorited('template-1')).toBe(false)
|
||||
})
|
||||
|
||||
it('should return false for non-existent template', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
expect(result.current.isFavorited('template-1')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('clear', () => {
|
||||
it('should clear all likedMap and favoritedMap', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
act(() => {
|
||||
result.current.setLikedStates({
|
||||
'template-1': true,
|
||||
'template-2': false,
|
||||
})
|
||||
result.current.setFavoritedStates({
|
||||
'template-1': true,
|
||||
'template-3': false,
|
||||
})
|
||||
})
|
||||
|
||||
expect(result.current.likedMap['template-1']).toBe(true)
|
||||
expect(result.current.favoritedMap['template-1']).toBe(true)
|
||||
|
||||
act(() => {
|
||||
result.current.clear()
|
||||
})
|
||||
|
||||
expect(result.current.likedMap).toEqual({})
|
||||
expect(result.current.favoritedMap).toEqual({})
|
||||
})
|
||||
|
||||
it('should allow setting states after clear', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
act(() => {
|
||||
result.current.setLiked('template-1', true)
|
||||
})
|
||||
|
||||
act(() => {
|
||||
result.current.clear()
|
||||
})
|
||||
|
||||
act(() => {
|
||||
result.current.setLiked('template-2', true)
|
||||
})
|
||||
|
||||
expect(result.current.likedMap['template-2']).toBe(true)
|
||||
expect(result.current.likedMap['template-1']).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('integration tests', () => {
|
||||
it('should handle mixed liked and favorited states', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
act(() => {
|
||||
result.current.setLiked('template-1', true)
|
||||
result.current.setFavorited('template-1', false)
|
||||
result.current.setLiked('template-2', false)
|
||||
result.current.setFavorited('template-2', true)
|
||||
})
|
||||
|
||||
expect(result.current.isLiked('template-1')).toBe(true)
|
||||
expect(result.current.isFavorited('template-1')).toBe(false)
|
||||
expect(result.current.isLiked('template-2')).toBe(false)
|
||||
expect(result.current.isFavorited('template-2')).toBe(true)
|
||||
})
|
||||
|
||||
it('should maintain independence between likedMap and favoritedMap', () => {
|
||||
const { result } = renderHook(() => useTemplateSocialStore())
|
||||
|
||||
act(() => {
|
||||
result.current.setLiked('template-1', true)
|
||||
})
|
||||
|
||||
act(() => {
|
||||
result.current.setFavorited('template-1', true)
|
||||
})
|
||||
|
||||
act(() => {
|
||||
result.current.clear()
|
||||
})
|
||||
|
||||
// clear 应该清空两个 map
|
||||
expect(result.current.isLiked('template-1')).toBe(false)
|
||||
expect(result.current.isFavorited('template-1')).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
76
stores/templateSocialStore.ts
Normal file
76
stores/templateSocialStore.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { create } from 'zustand'
|
||||
|
||||
interface TemplateSocialState {
|
||||
// 点赞状态缓存: templateId -> boolean
|
||||
likedMap: Record<string, boolean>
|
||||
|
||||
// 收藏状态缓存: templateId -> boolean
|
||||
favoritedMap: Record<string, boolean>
|
||||
|
||||
// 批量更新点赞状态
|
||||
setLikedStates: (states: Record<string, boolean>) => void
|
||||
|
||||
// 批量更新收藏状态
|
||||
setFavoritedStates: (states: Record<string, boolean>) => void
|
||||
|
||||
// 更新单个点赞状态
|
||||
setLiked: (templateId: string, liked: boolean) => void
|
||||
|
||||
// 更新单个收藏状态
|
||||
setFavorited: (templateId: string, favorited: boolean) => void
|
||||
|
||||
// 获取点赞状态
|
||||
isLiked: (templateId: string) => boolean
|
||||
|
||||
// 获取收藏状态
|
||||
isFavorited: (templateId: string) => boolean
|
||||
|
||||
// 清空所有缓存
|
||||
clear: () => void
|
||||
}
|
||||
|
||||
export const useTemplateSocialStore = create<TemplateSocialState>((set, get) => ({
|
||||
// State
|
||||
likedMap: {},
|
||||
favoritedMap: {},
|
||||
|
||||
// Actions
|
||||
setLikedStates: (states) => {
|
||||
set((state) => ({
|
||||
likedMap: { ...state.likedMap, ...states },
|
||||
}))
|
||||
},
|
||||
|
||||
setFavoritedStates: (states) => {
|
||||
set((state) => ({
|
||||
favoritedMap: { ...state.favoritedMap, ...states },
|
||||
}))
|
||||
},
|
||||
|
||||
setLiked: (templateId, liked) => {
|
||||
set((state) => ({
|
||||
likedMap: { ...state.likedMap, [templateId]: liked },
|
||||
}))
|
||||
},
|
||||
|
||||
setFavorited: (templateId, favorited) => {
|
||||
set((state) => ({
|
||||
favoritedMap: { ...state.favoritedMap, [templateId]: favorited },
|
||||
}))
|
||||
},
|
||||
|
||||
isLiked: (templateId) => {
|
||||
return get().likedMap[templateId] || false
|
||||
},
|
||||
|
||||
isFavorited: (templateId) => {
|
||||
return get().favoritedMap[templateId] || false
|
||||
},
|
||||
|
||||
clear: () => {
|
||||
set({
|
||||
likedMap: {},
|
||||
favoritedMap: {},
|
||||
})
|
||||
},
|
||||
}))
|
||||
Reference in New Issue
Block a user