fix: reduce useState and useEffect (#2223)

This commit is contained in:
Himself65
2023-05-04 17:53:52 -05:00
committed by GitHub
parent 52b9734a7b
commit 097cce34b5
5 changed files with 164 additions and 81 deletions

View File

@@ -3,6 +3,7 @@
*/ */
import 'fake-indexeddb/auto'; import 'fake-indexeddb/auto';
import { UNTITLED_WORKSPACE_NAME } from '@affine/env';
import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models'; import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models';
import type { Page } from '@blocksuite/store'; import type { Page } from '@blocksuite/store';
import { assertExists } from '@blocksuite/store'; import { assertExists } from '@blocksuite/store';
@@ -10,7 +11,7 @@ import { Workspace as BlockSuiteWorkspace } from '@blocksuite/store';
import { renderHook } from '@testing-library/react'; import { renderHook } from '@testing-library/react';
import { useBlockSuiteWorkspacePageIsPublic } from '@toeverything/hooks/use-block-suite-workspace-page-is-public'; import { useBlockSuiteWorkspacePageIsPublic } from '@toeverything/hooks/use-block-suite-workspace-page-is-public';
import { useBlockSuiteWorkspacePageTitle } from '@toeverything/hooks/use-block-suite-workspace-page-title'; import { useBlockSuiteWorkspacePageTitle } from '@toeverything/hooks/use-block-suite-workspace-page-title';
import { describe, expect, test } from 'vitest'; import { describe, expect, test, vitest } from 'vitest';
import { beforeEach } from 'vitest'; import { beforeEach } from 'vitest';
import { useBlockSuiteWorkspaceName } from '../use-block-suite-workspace-name'; import { useBlockSuiteWorkspaceName } from '../use-block-suite-workspace-name';
@@ -30,9 +31,9 @@ beforeEach(async () => {
const frameId = page.addBlock('affine:frame', {}, pageBlockId); const frameId = page.addBlock('affine:frame', {}, pageBlockId);
page.addBlock('affine:paragraph', {}, frameId); page.addBlock('affine:paragraph', {}, frameId);
}; };
initPage(blockSuiteWorkspace.createPage('page0')); initPage(blockSuiteWorkspace.createPage({ id: 'page0' }));
initPage(blockSuiteWorkspace.createPage('page1')); initPage(blockSuiteWorkspace.createPage({ id: 'page1' }));
initPage(blockSuiteWorkspace.createPage('page2')); initPage(blockSuiteWorkspace.createPage({ id: 'page2' }));
}); });
describe('useBlockSuiteWorkspaceName', () => { describe('useBlockSuiteWorkspaceName', () => {
@@ -48,6 +49,17 @@ describe('useBlockSuiteWorkspaceName', () => {
workspaceNameHook.result.current[1]('test 3'); workspaceNameHook.result.current[1]('test 3');
expect(blockSuiteWorkspace.meta.name).toBe('test 3'); expect(blockSuiteWorkspace.meta.name).toBe('test 3');
}); });
test('null', () => {
const workspaceNameHook = renderHook(() =>
useBlockSuiteWorkspaceName(null)
);
vitest.spyOn(globalThis.console, 'warn');
expect(workspaceNameHook.result.current[0]).toBe(UNTITLED_WORKSPACE_NAME);
workspaceNameHook.result.current[1]('test');
expect(globalThis.console.warn).toHaveBeenCalledTimes(2);
expect(workspaceNameHook.result.current[0]).toBe(UNTITLED_WORKSPACE_NAME);
});
}); });
describe('useBlockSuiteWorkspacePageTitle', () => { describe('useBlockSuiteWorkspacePageTitle', () => {

View File

@@ -1,32 +1,51 @@
import { UNTITLED_WORKSPACE_NAME } from '@affine/env'; import { UNTITLED_WORKSPACE_NAME } from '@affine/env';
import type { Workspace } from '@blocksuite/store'; import type { Workspace } from '@blocksuite/store';
import { assertExists } from '@blocksuite/store'; import { assertExists } from '@blocksuite/store';
import { useCallback, useEffect, useState } from 'react'; import type { Atom, WritableAtom } from 'jotai';
import { atom, useAtom } from 'jotai';
const weakMap = new WeakMap<
Workspace,
WritableAtom<string, [string], void> & Atom<string>
>();
const emptyWorkspaceNameAtom = atom(UNTITLED_WORKSPACE_NAME, () => {
console.warn('you cannot set the name of an null workspace.');
console.warn('this is a bug in the code.');
});
export function useBlockSuiteWorkspaceName( export function useBlockSuiteWorkspaceName(
blockSuiteWorkspace: Workspace | null blockSuiteWorkspace: Workspace | null
) { ) {
const [name, set] = useState( let nameAtom:
() => blockSuiteWorkspace?.meta.name ?? UNTITLED_WORKSPACE_NAME | (WritableAtom<string, [string], void> & Atom<string>)
); | undefined;
useEffect(() => { if (!blockSuiteWorkspace) {
if (blockSuiteWorkspace) { nameAtom = emptyWorkspaceNameAtom;
set(blockSuiteWorkspace.meta.name ?? ''); } else if (!weakMap.has(blockSuiteWorkspace)) {
const baseAtom = atom<string>(
blockSuiteWorkspace.meta.name ?? UNTITLED_WORKSPACE_NAME
);
const writableAtom = atom(
get => get(baseAtom),
(get, set, name: string) => {
blockSuiteWorkspace.meta.setName(name);
set(baseAtom, name);
}
);
baseAtom.onMount = set => {
const dispose = blockSuiteWorkspace.meta.commonFieldsUpdated.on(() => { const dispose = blockSuiteWorkspace.meta.commonFieldsUpdated.on(() => {
set(blockSuiteWorkspace.meta.name ?? ''); set(blockSuiteWorkspace.meta.name ?? '');
}); });
return () => { return () => {
dispose.dispose(); dispose.dispose();
}; };
} };
}, [blockSuiteWorkspace]); weakMap.set(blockSuiteWorkspace, writableAtom);
const setName = useCallback( nameAtom = writableAtom;
(name: string) => { } else {
assertExists(blockSuiteWorkspace); nameAtom = weakMap.get(blockSuiteWorkspace);
blockSuiteWorkspace.meta.setName(name); assertExists(nameAtom);
set(name); }
}, return useAtom(nameAtom);
[blockSuiteWorkspace]
);
return [name, setName] as const;
} }

View File

@@ -1,24 +1,36 @@
import type { Page } from '@blocksuite/store'; import type { Page } from '@blocksuite/store';
import { useCallback, useEffect, useState } from 'react'; import { assertExists } from '@blocksuite/store';
import type { Atom, WritableAtom } from 'jotai';
import { atom, useAtom } from 'jotai';
const weakMap = new WeakMap<
Page,
WritableAtom<boolean, [boolean], void> & Atom<boolean>
>();
export function useBlockSuiteWorkspacePageIsPublic(page: Page) { export function useBlockSuiteWorkspacePageIsPublic(page: Page) {
const [isPublic, set] = useState<boolean>(() => page.meta.isPublic ?? false); if (!weakMap.has(page)) {
useEffect(() => { const baseAtom = atom<boolean>(page.meta.isPublic ?? false);
const disposable = page.workspace.meta.pageMetasUpdated.on(() => { const writableAtom = atom(
set(page.meta.isPublic ?? false); get => get(baseAtom),
}); (get, set, isPublic: boolean) => {
return () => { page.workspace.setPageMeta(page.id, {
disposable.dispose(); isPublic,
}; });
}, [page]); set(baseAtom, isPublic);
const setIsPublic = useCallback( }
(isPublic: boolean) => { );
set(isPublic); baseAtom.onMount = set => {
page.workspace.setPageMeta(page.id, { const disposable = page.workspace.meta.pageMetasUpdated.on(() => {
isPublic, set(page.meta.isPublic ?? false);
}); });
}, return () => {
[page.id, page.workspace] disposable.dispose();
); };
return [isPublic, setIsPublic] as const; };
weakMap.set(page, writableAtom);
}
const isPublicAtom = weakMap.get(page);
assertExists(isPublicAtom);
return useAtom(isPublicAtom);
} }

View File

@@ -1,25 +1,40 @@
import type { Workspace } from '@blocksuite/store'; import type { Workspace } from '@blocksuite/store';
import { assertExists } from '@blocksuite/store'; import { assertExists } from '@blocksuite/store';
import { useEffect, useState } from 'react'; import type { Atom } from 'jotai';
import { atom, useAtomValue } from 'jotai';
const weakMap = new WeakMap<Workspace, Map<string, Atom<string>>>();
function getAtom(w: Workspace, pageId: string): Atom<string> {
if (!weakMap.has(w)) {
weakMap.set(w, new Map());
}
const map = weakMap.get(w);
assertExists(map);
if (!map.has(pageId)) {
const baseAtom = atom<string>(w.getPage(pageId)?.meta.title || 'Untitled');
baseAtom.onMount = set => {
const disposable = w.meta.pageMetasUpdated.on(() => {
const page = w.getPage(pageId);
assertExists(page);
set(page?.meta.title || 'Untitled');
});
return () => {
disposable.dispose();
};
};
map.set(pageId, baseAtom);
return baseAtom;
} else {
return map.get(pageId) as Atom<string>;
}
}
export function useBlockSuiteWorkspacePageTitle( export function useBlockSuiteWorkspacePageTitle(
blockSuiteWorkspace: Workspace, blockSuiteWorkspace: Workspace,
pageId: string pageId: string
) { ) {
const page = blockSuiteWorkspace.getPage(pageId); const titleAtom = getAtom(blockSuiteWorkspace, pageId);
const [title, setTitle] = useState(() => page?.meta.title || 'AFFiNE'); assertExists(titleAtom);
useEffect(() => { return useAtomValue(titleAtom);
const page = blockSuiteWorkspace.getPage(pageId);
setTitle(page?.meta.title || 'Untitled');
const dispose = blockSuiteWorkspace.meta.pageMetasUpdated.on(() => {
const page = blockSuiteWorkspace.getPage(pageId);
assertExists(page);
setTitle(page?.meta.title || 'Untitled');
});
return () => {
dispose.dispose();
};
}, [blockSuiteWorkspace, pageId]);
return title;
} }

View File

@@ -1,30 +1,55 @@
import type { Page, Workspace } from '@blocksuite/store'; import type { Page, Workspace } from '@blocksuite/store';
import { useEffect, useState } from 'react'; import { assertExists, DisposableGroup } from '@blocksuite/store';
import type { Atom } from 'jotai';
import { atom, useAtomValue } from 'jotai';
const weakMap = new WeakMap<Workspace, Map<string, Atom<Page | null>>>();
const emptyAtom = atom<Page | null>(null);
function getAtom(w: Workspace, pageId: string | null): Atom<Page | null> {
if (!pageId) {
return emptyAtom;
}
if (!weakMap.has(w)) {
weakMap.set(w, new Map());
}
const map = weakMap.get(w);
assertExists(map);
if (!map.has(pageId)) {
const baseAtom = atom(w.getPage(pageId));
baseAtom.onMount = set => {
const group = new DisposableGroup();
group.add(
w.slots.pageAdded.on(id => {
if (pageId === id) {
set(w.getPage(id));
}
})
);
group.add(
w.slots.pageRemoved.on(id => {
if (pageId === id) {
set(null);
}
})
);
return () => {
group.dispose();
};
};
map.set(pageId, baseAtom);
return baseAtom;
} else {
return map.get(pageId) as Atom<Page | null>;
}
}
export function useBlockSuiteWorkspacePage( export function useBlockSuiteWorkspacePage(
blockSuiteWorkspace: Workspace, blockSuiteWorkspace: Workspace,
pageId: string | null pageId: string | null
): Page | null { ): Page | null {
const [page, setPage] = useState(() => { const pageAtom = getAtom(blockSuiteWorkspace, pageId);
if (pageId === null) { assertExists(pageAtom);
return null; return useAtomValue(pageAtom);
}
return blockSuiteWorkspace.getPage(pageId);
});
useEffect(() => {
if (pageId) {
setPage(blockSuiteWorkspace.getPage(pageId));
}
}, [blockSuiteWorkspace, pageId]);
useEffect(() => {
const disposable = blockSuiteWorkspace.slots.pageAdded.on(id => {
if (pageId === id) {
setPage(blockSuiteWorkspace.getPage(id));
}
});
return () => {
disposable.dispose();
};
}, [blockSuiteWorkspace, pageId]);
return page;
} }