Skip to content

Commit 8b35368

Browse files
committed
[hooks] useStores
1 parent ea088c8 commit 8b35368

File tree

6 files changed

+74
-0
lines changed

6 files changed

+74
-0
lines changed

src/@types/ui-react/docs.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,47 @@
397397
* @since v1.0.0
398398
*/
399399
/// useStore
400+
/**
401+
* The useStores hook is used to get a reference to all the Store objects named
402+
* by Id within a Provider component context.
403+
*
404+
* A Provider component is used to wrap part of an application in a context. It
405+
* can contain a default Store (or a set of Store objects named by Id) that can
406+
* be easily accessed without having to be passed down as props through every
407+
* component.
408+
*
409+
* The useStores hook lets you get a reference to the latter as an object.
410+
* @returns An object containing all the Store objects named by Id.
411+
* @example
412+
* This example creates a Provider context into which a Store is provided, named
413+
* by Id. A component within it then uses the useStores hook to get a reference
414+
* to the Store again.
415+
*
416+
* ```jsx
417+
* import {Provider, useStores} from 'tinybase/ui-react';
418+
* import React from 'react';
419+
* import {createRoot} from 'react-dom/client';
420+
* import {createStore} from 'tinybase';
421+
*
422+
* const App = ({store}) => (
423+
* <Provider storesById={{petStore: store}}>
424+
* <Pane />
425+
* </Provider>
426+
* );
427+
* const Pane = () => (
428+
* <span>{useStores()['petStore'].getListenerStats().tables}</span>
429+
* );
430+
*
431+
* const store = createStore();
432+
* const app = document.createElement('div');
433+
* createRoot(app).render(<App store={store} />); // !act
434+
* console.log(app.innerHTML);
435+
* // -> '<span>0</span>'
436+
* ```
437+
* @category Store hooks
438+
* @since v5.4.1
439+
*/
440+
/// useStores
400441
/**
401442
* The useStoreOrStoreById hook is used to get a reference to a Store object
402443
* from within a Provider component context, _or_ have it passed directly to

src/@types/ui-react/index.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ export function useStoreIds(): Ids;
130130
/// useStore
131131
export function useStore(id?: Id): Store | undefined;
132132

133+
/// useStores
134+
export function useStores(): {[storeId: Id]: Store};
135+
133136
/// useStoreOrStoreById
134137
export function useStoreOrStoreById(
135138
storeOrStoreId?: StoreOrStoreId,

src/@types/ui-react/with-schemas/index.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,9 @@ export type WithSchemas<Schemas extends OptionalSchemas> = {
178178
/// useStore
179179
useStore: (id?: Id) => Store<Schemas> | undefined;
180180

181+
/// useStores
182+
useStores: () => {[storeId: Id]: Store<OptionalSchemas>};
183+
181184
/// useStoreOrStoreById
182185
useStoreOrStoreById: (
183186
storeOrStoreId?: StoreOrStoreId<Schemas>,

src/ui-react/context.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import type {
2121
useRelationshipsIds as useRelationshipsIdsDecl,
2222
useStore as useStoreDecl,
2323
useStoreIds as useStoreIdsDecl,
24+
useStores as useStoresDecl,
2425
useSynchronizer as useSynchronizerDecl,
2526
useSynchronizerIds as useSynchronizerIdsDecl,
2627
} from '../@types/ui-react/index.d.ts';
@@ -118,6 +119,11 @@ const useThing = <UsedThing extends Thing>(
118119
) as UsedThing;
119120
};
120121

122+
const useThings = <UsedThing extends Thing>(
123+
offset: Offsets,
124+
): IdObj<UsedThing> =>
125+
({...(useContext(Context)[offset * 2 + 1] ?? {})}) as IdObj<UsedThing>;
126+
121127
const useThingOrThingById = <
122128
Thing extends
123129
| Store
@@ -159,6 +165,9 @@ export const useStoreIds: typeof useStoreIdsDecl = () =>
159165
export const useStore: typeof useStoreDecl = (id?: Id): Store | undefined =>
160166
useThing(id, Offsets.Store);
161167

168+
export const useStores: typeof useStoresDecl = (): IdObj<Store> =>
169+
useThings(Offsets.Store);
170+
162171
export const useStoreOrStoreById = (
163172
storeOrStoreId?: StoreOrStoreId,
164173
): Store | undefined => useThingOrThingById(storeOrStoreId, Offsets.Store);

src/ui-react/hooks.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ export {
302302
useStore,
303303
useStoreIds,
304304
useStoreOrStoreById,
305+
useStores,
305306
useSynchronizer,
306307
useSynchronizerIds,
307308
useSynchronizerOrSynchronizerById,

test/unit/core/ui-react/hooks.test.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ import {
122122
useStartTransactionListener,
123123
useStore,
124124
useStoreIds,
125+
useStores,
125126
useTable,
126127
useTableCellIds,
127128
useTableCellIdsListener,
@@ -888,6 +889,22 @@ describe('Context Hooks', () => {
888889
expect(didRender).toHaveBeenCalledTimes(1);
889890
});
890891

892+
test('useStores', () => {
893+
const Test = () =>
894+
didRender(<>{JSON.stringify(useStores()?.['store1']?.getTables())}</>);
895+
const store1 = createStore().setTables({t1: {r1: {c1: 2}}});
896+
const store2 = createStore();
897+
act(() => {
898+
renderer = create(
899+
<Provider storesById={{store1, store2}}>
900+
<Test />
901+
</Provider>,
902+
);
903+
});
904+
expect(renderer.toJSON()).toEqual('{"t1":{"r1":{"c1":2}}}');
905+
expect(didRender).toHaveBeenCalledTimes(1);
906+
});
907+
891908
test('useStoreIds', () => {
892909
const Test = () => didRender(<>{JSON.stringify(useStoreIds())}</>);
893910
const store1 = createStore();

0 commit comments

Comments
 (0)