This commit is contained in:
imeepos
2026-01-28 18:46:44 +08:00
parent e478b126cd
commit 7c50d396e9
40 changed files with 6567 additions and 39 deletions

View 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 }
}

View 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)
})
})
})

View 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: {},
})
},
}))