diff --git a/src/types/docs/ui-react.js b/src/types/docs/ui-react.js
index eed3053d1aa..44ac94490ef 100644
--- a/src/types/docs/ui-react.js
+++ b/src/types/docs/ui-react.js
@@ -9366,11 +9366,9 @@
*
* It is possible to create a Persister outside of the React app with the
* regular createPersister function and pass it in, but you may prefer to create
- * it within the app, perhaps inside the top-level component. To defend against
- * a new Persister being created every time the app renders or re-renders, the
- * useCreatePersister hook wraps the creation in a memoization, and provides a
- * second callback so that you can configure the Persister, once, and
- * asynchronously, when it is created.
+ * it within the app, perhaps inside the top-level component. To prevent a new
+ * Persister being created every time the app renders or re-renders, since v5.0
+ * the useCreateMetrics hook performs the creation in an effect.
*
* If your `create` function (the second parameter to the hook) contains
* dependencies, the changing of which should cause the Persister to be
@@ -9378,19 +9376,18 @@
* you would for any React hook with dependencies. The Store passed in as the
* first parameter of this hook is used as a dependency by default.
*
- * A second `then` callback can be provided as the fourth parameter. This is
- * called after the creation, and, importantly, can be asynchronous, so that you
- * can configure the Persister with the startAutoLoad method and startAutoSave
- * method, for example. If this callback contains dependencies, the changing of
- * which should cause the Persister to be reconfigured, you can provide them in
- * an array in the fifth parameter. The Persister itself is used as a dependency
- * by default.
+ * A second callback, called `then`, can be provided as the fourth parameter.
+ * This is called after the creation, and, importantly, can be asynchronous, so
+ * that you can configure the Persister with the startAutoLoad method and
+ * startAutoSave method, for example. If this callback contains dependencies,
+ * the changing of which should cause the Persister to be reconfigured, you can
+ * provide them in an array in the fifth parameter. The Persister itself is used
+ * as a dependency by default.
*
* Since v4.3.0, the `create` function can return undefined, meaning that you
* can enable or disable persistence conditionally within this hook. This is
* useful for applications which might turn on or off their cloud persistence or
- * collaboration features. As a result, the `then` callback may also get passed
- * undefined, which you should handle accordingly.
+ * collaboration features.
*
* Since v4.3.19, a `destroy` function can be provided which will be called
* after an old Persister is destroyed due to a change in the `createDeps`
@@ -9509,8 +9506,8 @@
* // -> '{\"pets\":{\"fido\":{\"species\":\"dog\"}}}'
*
* root.render(); // !act
- * // -> 'Persister created for session key cujoStore'
* // -> 'Persister destroyed for session key fidoStore'
+ * // -> 'Persister created for session key cujoStore'
*
* // ... // !act
* console.log(app.innerHTML);
diff --git a/src/types/ui-react.d.ts b/src/types/ui-react.d.ts
index 5426e5b3474..67d58792cd5 100644
--- a/src/types/ui-react.d.ts
+++ b/src/types/ui-react.d.ts
@@ -959,9 +959,9 @@ export function useCreatePersister<
store: Store,
create: (store: Store) => PersisterOrUndefined,
createDeps?: React.DependencyList,
- then?: (persister: PersisterOrUndefined) => Promise,
+ then?: (persister: Persister) => Promise,
thenDeps?: React.DependencyList,
- destroy?: (persister: PersisterOrUndefined) => void,
+ destroy?: (persister: Persister) => void,
destroyDeps?: React.DependencyList,
): PersisterOrUndefined;
diff --git a/src/types/with-schemas/ui-react.d.ts b/src/types/with-schemas/ui-react.d.ts
index de3126a78f7..688141c573c 100644
--- a/src/types/with-schemas/ui-react.d.ts
+++ b/src/types/with-schemas/ui-react.d.ts
@@ -1143,9 +1143,9 @@ export type WithSchemas = {
store: Store,
create: (store: Store) => PersisterOrUndefined,
createDeps?: React.DependencyList,
- then?: (persister?: PersisterOrUndefined) => Promise,
+ then?: (persister: Persister) => Promise,
thenDeps?: React.DependencyList,
- destroy?: (persister: PersisterOrUndefined) => void,
+ destroy?: (persister: Persister) => void,
destroyDeps?: React.DependencyList,
) => PersisterOrUndefined;
diff --git a/src/ui-react/hooks.ts b/src/ui-react/hooks.ts
index f90f90c3096..622cbcd1aa0 100644
--- a/src/ui-react/hooks.ts
+++ b/src/ui-react/hooks.ts
@@ -1741,32 +1741,33 @@ export const useCreatePersister: typeof useCreatePersisterDecl = <
store: Store,
create: (store: Store) => PersisterOrUndefined,
createDeps: React.DependencyList = EMPTY_ARRAY,
- then?: (persister: PersisterOrUndefined) => Promise,
+ then?: (persister: Persister) => Promise,
thenDeps: React.DependencyList = EMPTY_ARRAY,
- destroy?: (persister: PersisterOrUndefined) => void,
+ destroy?: (persister: Persister) => void,
destroyDeps: React.DependencyList = EMPTY_ARRAY,
): PersisterOrUndefined => {
const [, rerender] = useState<[]>();
- const persister = useMemo(
- () => create(store) as any,
- // eslint-disable-next-line react-hooks/exhaustive-deps
- [store, ...createDeps],
- );
+ const [persister, setPersister] = useState();
+
useEffect(
() => {
- (async () => {
+ const newPersister = create(store);
+ setPersister(newPersister);
+ if (newPersister) {
if (then) {
- await then(persister);
- rerender([]);
+ (async () => {
+ await then(newPersister);
+ rerender([]);
+ })();
}
- })();
- return () => {
- persister?.destroy();
- destroy?.(persister);
- };
+ return () => {
+ newPersister.destroy();
+ destroy?.(newPersister);
+ };
+ }
},
// eslint-disable-next-line react-hooks/exhaustive-deps
- [persister, ...thenDeps, ...destroyDeps],
+ [store, ...createDeps, ...thenDeps, ...destroyDeps],
);
return persister;
};
diff --git a/test/unit/ui-react/hooks.test.tsx b/test/unit/ui-react/hooks.test.tsx
index 57182050982..ee2962fd9d8 100644
--- a/test/unit/ui-react/hooks.test.tsx
+++ b/test/unit/ui-react/hooks.test.tsx
@@ -407,9 +407,11 @@ describe('Create Hooks', () => {
});
const Test = ({id}: {id: number}) => {
const store = useCreateStore(initStore);
- const persister = useCreatePersister(store, createPersister);
+ const persister = useCreatePersister(store, createPersister, [id]);
const cell = useCell('t1', 'r1', 'c1', store);
- return didRender(<>{JSON.stringify([id, persister.getStats(), cell])}>);
+ return didRender(
+ <>{JSON.stringify([id, persister?.getStats(), cell])}>,
+ );
};
act(() => {
renderer = create();
@@ -423,9 +425,18 @@ describe('Create Hooks', () => {
expect(renderer.toJSON()).toEqual(
JSON.stringify([1, {loads: 1, saves: 0}, 1]),
);
+ act(() => {
+ renderer.update();
+ });
+ await act(async () => {
+ await pause();
+ });
+ expect(renderer.toJSON()).toEqual(
+ JSON.stringify([2, {loads: 0, saves: 0}, 1]),
+ );
expect(initStore).toHaveBeenCalledTimes(1);
- expect(createPersister).toHaveBeenCalledTimes(1);
- expect(didRender).toHaveBeenCalledTimes(2);
+ expect(createPersister).toHaveBeenCalledTimes(2);
+ expect(didRender).toHaveBeenCalledTimes(5);
_persister?.stopAutoLoad()?.stopAutoSave();
});
});
@@ -435,9 +446,11 @@ test('useCreatePersister, then, no destroy', async () => {
tmp.setGracefulCleanup();
const fileName = tmp.fileSync().name;
const initStore = jest.fn(createStore);
- const createPersister = jest.fn((store: Store) => {
- _persister = createFilePersister(store, fileName);
- return _persister;
+ const createPersister = jest.fn((store: Store, id: number) => {
+ if (id != 0) {
+ _persister = createFilePersister(store, fileName);
+ return _persister;
+ }
});
const initPersister = jest.fn(async (persister: Persister, id: number) => {
await persister.load({t1: {r1: {c1: id}}});
@@ -446,17 +459,20 @@ test('useCreatePersister, then, no destroy', async () => {
const store = useCreateStore(initStore);
const persister = useCreatePersister(
store,
- (store) => createPersister(store),
- undefined,
+ (store) => createPersister(store, id),
+ [id],
async (persister) => await initPersister(persister, id),
[id],
);
- return didRender(<>{JSON.stringify([id, persister.getStats()])}>);
+ return didRender(<>{JSON.stringify([id, persister?.getStats()])}>);
};
+ act(() => {
+ renderer = create();
+ });
+ expect(renderer.toJSON()).toEqual(JSON.stringify([0, null]));
act(() => {
renderer = create();
});
- expect(renderer.toJSON()).toEqual(JSON.stringify([1, {loads: 0, saves: 0}]));
await act(async () => {
await pause();
});
@@ -464,15 +480,14 @@ test('useCreatePersister, then, no destroy', async () => {
act(() => {
renderer.update();
});
- expect(renderer.toJSON()).toEqual(JSON.stringify([2, {loads: 1, saves: 0}]));
await act(async () => {
await pause();
});
- expect(renderer.toJSON()).toEqual(JSON.stringify([2, {loads: 2, saves: 0}]));
- expect(initStore).toHaveBeenCalledTimes(1);
- expect(createPersister).toHaveBeenCalledTimes(1);
+ expect(renderer.toJSON()).toEqual(JSON.stringify([2, {loads: 1, saves: 0}]));
+ expect(initStore).toHaveBeenCalledTimes(2);
+ expect(createPersister).toHaveBeenCalledTimes(3);
expect(initPersister).toHaveBeenCalledTimes(2);
- expect(didRender).toHaveBeenCalledTimes(4);
+ expect(didRender).toHaveBeenCalledTimes(7);
_persister?.stopAutoLoad()?.stopAutoSave();
});
@@ -502,12 +517,11 @@ test('useCreatePersister, then, destroy', async () => {
[id],
destroyPersister,
);
- return didRender(<>{JSON.stringify([id, persister.getStats()])}>);
+ return didRender(<>{JSON.stringify([id, persister?.getStats()])}>);
};
act(() => {
renderer = create();
});
- expect(renderer.toJSON()).toEqual(JSON.stringify([1, {loads: 0, saves: 0}]));
await act(async () => {
await pause();
});
@@ -515,7 +529,6 @@ test('useCreatePersister, then, destroy', async () => {
act(() => {
renderer.update();
});
- expect(renderer.toJSON()).toEqual(JSON.stringify([2, {loads: 0, saves: 0}]));
await act(async () => {
await pause();
});
@@ -525,7 +538,7 @@ test('useCreatePersister, then, destroy', async () => {
expect(initPersister).toHaveBeenCalledTimes(2);
expect(destroyPersister).toHaveBeenCalledTimes(1);
expect(destroyPersister).toHaveBeenCalledWith(persisters[1]);
- expect(didRender).toHaveBeenCalledTimes(4);
+ expect(didRender).toHaveBeenCalledTimes(6);
persisters.forEach((persister) => persister.stopAutoLoad().stopAutoSave());
});