diff --git a/packages/common/infra/src/livedata/__tests__/react.spec.tsx b/packages/common/infra/src/livedata/__tests__/react.spec.tsx
index cfe13a736f..a3378bc515 100644
--- a/packages/common/infra/src/livedata/__tests__/react.spec.tsx
+++ b/packages/common/infra/src/livedata/__tests__/react.spec.tsx
@@ -1,31 +1,155 @@
/**
* @vitest-environment happy-dom
*/
-import { render, screen } from '@testing-library/react';
-import { useRef } from 'react';
+
+import { cleanup, render, screen, waitFor } from '@testing-library/react';
+import { useMemo } from 'react';
+import type { Subscriber } from 'rxjs';
import { Observable } from 'rxjs';
-import { describe, expect, test, vi } from 'vitest';
+import { afterEach, describe, expect, test, vi } from 'vitest';
import { LiveData, useLiveData } from '..';
describe('livedata', () => {
- test('react', () => {
+ afterEach(() => {
+ cleanup();
+ });
+ test('react', async () => {
const livedata$ = new LiveData(0);
+ let renderCount = 0;
const Component = () => {
- const renderCount = useRef(0);
- renderCount.current++;
+ renderCount++;
const value = useLiveData(livedata$);
- return (
-
- {renderCount.current}:{value}
-
+ return {value};
+ };
+ render();
+ expect(screen.getByRole('main').innerText).toBe('0');
+ livedata$.next(1);
+ // wait for rerender
+ await waitFor(() => expect(screen.getByRole('main').innerText).toBe('1'));
+ expect(renderCount).toBe(2);
+ });
+
+ test('react livedata.map', async () => {
+ const livedata$ = new LiveData(0);
+ let renderCount = 0;
+ const Component = () => {
+ renderCount++;
+ const value = useLiveData(livedata$.map(v => v + 1));
+ return {value};
+ };
+ render();
+ expect(screen.getByRole('main').innerText).toBe('1');
+ livedata$.next(1);
+ // wait for rerender
+ await waitFor(() => expect(screen.getByRole('main').innerText).toBe('2'));
+ expect(renderCount).toBe(2);
+ });
+
+ test('react livedata.map heavy object copy', async () => {
+ const livedata$ = new LiveData({ hello: 'world' });
+ let renderCount = 0;
+ let objectCopyCount = 0;
+ const Component = () => {
+ renderCount++;
+
+ const value = useLiveData(
+ livedata$.map(v => {
+ objectCopyCount++;
+ return { ...v };
+ })
);
+ return {value.hello};
};
const { rerender } = render();
- expect(screen.getByRole('main').innerText).toBe('1:0');
- livedata$.next(1);
+ expect(screen.getByRole('main').innerText).toBe('world');
+ livedata$.next({ hello: 'foobar' });
+ // wait for rerender
+ await waitFor(() =>
+ expect(screen.getByRole('main').innerText).toBe('foobar')
+ );
+ expect(renderCount).toBe(2);
+ expect(objectCopyCount).toBe(3);
+
rerender();
- expect(screen.getByRole('main').innerText).toBe('3:1');
+ expect(renderCount).toBe(3);
+ expect(objectCopyCount).toBe(4);
+ });
+
+ test('react useMemo livedata.map heavy object copy', async () => {
+ const livedata$ = new LiveData({ hello: 'world' });
+ let renderCount = 0;
+ let objectCopyCount = 0;
+ const Component = () => {
+ renderCount++;
+ const value = useLiveData(
+ useMemo(
+ () =>
+ livedata$.map(v => {
+ objectCopyCount++;
+ return { ...v };
+ }),
+ []
+ )
+ );
+ return {value.hello};
+ };
+ const { rerender } = render();
+ expect(screen.getByRole('main').innerText).toBe('world');
+ livedata$.next({ hello: 'foobar' });
+ // wait for rerender
+ await waitFor(() =>
+ expect(screen.getByRole('main').innerText).toBe('foobar')
+ );
+ expect(renderCount).toBe(2);
+ expect(objectCopyCount).toBe(2);
+
+ rerender();
+ expect(renderCount).toBe(3);
+ expect(objectCopyCount).toBe(2);
+ });
+
+ test('react useLiveData with livedata from observable', async () => {
+ let subscribeCount = 0;
+ let renderCount = 0;
+
+ let innerSubscriber: Subscriber<{
+ value: number;
+ }> = null!;
+
+ const livedata$ = LiveData.from(
+ new Observable<{ value: number }>(subscriber => {
+ subscribeCount++;
+ subscriber.next({ value: 1 });
+ innerSubscriber = subscriber;
+ }),
+ { value: 0 }
+ );
+
+ const Component = () => {
+ renderCount++;
+ const value = useLiveData(
+ livedata$.map(v => ({
+ value: v.value + 1,
+ }))
+ ).value;
+ return {value};
+ };
+ const { rerender } = render();
+ expect(screen.getByRole('main').innerText).toBe('2');
+
+ expect(subscribeCount).toBe(1);
+ expect(renderCount).toBe(1);
+
+ innerSubscriber.next({ value: 2 });
+
+ await waitFor(() => expect(screen.getByRole('main').innerText).toBe('3'));
+ expect(subscribeCount).toBe(1);
+ expect(renderCount).toBe(2);
+
+ rerender();
+ expect(subscribeCount).toBe(1);
+ expect(renderCount).toBe(3);
});
test('lifecycle', async () => {
@@ -34,7 +158,6 @@ describe('livedata', () => {
const observable$ = new Observable(subscriber => {
observableSubscribed = true;
subscriber.next(1);
- console.log(1);
return () => {
observableClosed = true;
};