From d0e5e9f22e8db1b758f5a58965524451449f9b6b Mon Sep 17 00:00:00 2001 From: Xuan <97ssps30212@gmail.com> Date: Fri, 16 Jun 2023 12:01:59 +0800 Subject: [PATCH] feat: add useHydrate to support ssr --- src/__tests__/useHydrate.test.tsx | 49 +++++++++++++++++++++++++++++++ src/lib/hooks/index.ts | 1 + src/lib/hooks/useHydrate.ts | 8 +++++ 3 files changed, 58 insertions(+) create mode 100644 src/__tests__/useHydrate.test.tsx create mode 100644 src/lib/hooks/useHydrate.ts diff --git a/src/__tests__/useHydrate.test.tsx b/src/__tests__/useHydrate.test.tsx new file mode 100644 index 00000000..bfd7a17c --- /dev/null +++ b/src/__tests__/useHydrate.test.tsx @@ -0,0 +1,49 @@ +import { act, render } from '@testing-library/react'; +import { useAccessor, useHydrate } from '../lib'; +import type { Post } from './types'; +import { createPost, createPostModel, createPostModelControl, sleep } from './utils'; +import { renderToString } from 'react-dom/server'; + +describe('useHydrate', () => { + test('should hydrate successfully', async () => { + const onSuccessMock = vi.fn(); + const updateMock = vi.fn(); + const control = createPostModelControl({ onSuccessMock }); + const { getPostById, postAdapter, postModel } = createPostModel(control); + function Component({ postId }: { postId: number }) { + const { data } = useAccessor(getPostById(postId), postAdapter.tryReadOneFactory(postId), { + revalidateIfStale: false, + }); + + return
title: {data?.title}
; + } + function Page({ post }: { post: Post }) { + useHydrate(post, () => { + postModel.mutate(draft => { + postAdapter.createOne(draft, post); + updateMock(); + }); + }); + + return ; + } + + let ui = ; + const container = document.createElement('div'); + document.body.append(container); + container.innerHTML = renderToString(ui); + const { getByText, rerender } = render(ui, { container, hydrate: true }); + + getByText('title: title0'); + await act(() => sleep(10)); + expect(onSuccessMock).toHaveBeenCalledTimes(0); + expect(updateMock).toHaveBeenCalledTimes(1); + + ui = ; + rerender(ui); + getByText('title: title1'); + await act(() => sleep(10)); + expect(onSuccessMock).toHaveBeenCalledTimes(0); + expect(updateMock).toHaveBeenCalledTimes(2); + }); +}); diff --git a/src/lib/hooks/index.ts b/src/lib/hooks/index.ts index 2f912ed9..8855e9c0 100644 --- a/src/lib/hooks/index.ts +++ b/src/lib/hooks/index.ts @@ -1 +1,2 @@ export * from './useAccessor'; +export * from './useHydrate'; diff --git a/src/lib/hooks/useHydrate.ts b/src/lib/hooks/useHydrate.ts new file mode 100644 index 00000000..cb0376be --- /dev/null +++ b/src/lib/hooks/useHydrate.ts @@ -0,0 +1,8 @@ +const dataset = new WeakSet(); + +export function useHydrate(data: T, update: () => void): void { + if (!dataset.has(data)) { + dataset.add(data); + update(); + } +}