@@ -212,6 +212,94 @@ const App = () => (
212212);
213213```
214214
215+ ## Defining Schema-Based Stores in React Components
216+
217+ One final pattern to be aware of is that you can use the ` WithSchemas ` type to
218+ create a schema-based Store in a non-rendering React component. This allows you
219+ to create a Store with a schema, define its persistence behavior, publish it
220+ into the context, and even expose domain-specific hooks - all in a single
221+ self-contained file.
222+
223+ There are good examples of this in the [ TinyHub] ( https://tinyhub.org/ ) project
224+ (see the store components [ this
225+ folder] ( https://github.com/tinyplex/tinyhub/tree/main/client/src/stores ) ).
226+
227+ If you want to use this pattern your app's top-level will look something like this:
228+
229+ ``` tsx yolo
230+ export const App = () => {
231+ return (
232+ <Provider >
233+ <MyStore /> { /* This is a schema-based Store component */ }
234+ <ActualApp /> { /* This is the actual rendered app */ }
235+ </Provider >
236+ );
237+ };
238+ ```
239+
240+ The ` MyStore ` component renders ` null ` so it doesn't appear visually, but it is
241+ responsible for creating the Store and making it available to the rest of the
242+ app via the Provider context that wraps them both.
243+
244+ The ` MyStore.tsx ` file might look something like this. (In this simplified case,
245+ we are just typing key values in our Store but of course this would work for a
246+ tabular Store too.)
247+
248+ ``` tsx yolo
249+ // A unique Id for this Store.
250+ const STORE_ID = ' myStore' ;
251+
252+ // The schema for this Store.
253+ const VALUES_SCHEMA = {
254+ myStringValue: {type: ' string' , default: ' foo' },
255+ myNumericValue: {type: ' number' , default: 42 },
256+ } as const ;
257+ type Schemas = [NoTablesSchema , typeof VALUES_SCHEMA ];
258+
259+ // Destructure the ui-react module with the schema applied.
260+ const {useCreateStore, useProvideStore, useCreatePersister, useValue} =
261+ UiReact as UiReact .WithSchemas <Schemas >;
262+
263+ export const MyStore = () => {
264+ // Create the Store and set its schema
265+ const myStore = useCreateStore (() =>
266+ createStore ().setValuesSchema (VALUES_SCHEMA ),
267+ );
268+
269+ // Create a local storage persister for the Store and start it
270+ useCreatePersister (
271+ settingsStore ,
272+ (settingsStore ) => createLocalPersister (settingsStore , STORE_ID ),
273+ [],
274+ async (persister ) => {
275+ await persister .startAutoLoad ();
276+ await persister .startAutoSave ();
277+ },
278+ );
279+
280+ // Provide the Store for the rest of the app.
281+ useProvideStore (STORE_ID , settingsStore );
282+
283+ // Don't render anything.
284+ return null ;
285+ };
286+ ```
287+
288+ In that same file, you can also define domain-specific hooks that use an expose
289+ the schema. For example, this hook will be typed to return a string if passed
290+ 'myStringValue', and a number if passed 'myNumericValue'.
291+
292+ ``` tsx yolo
293+ type ValueIds = keyof typeof VALUES_SCHEMA ;
294+ export const useSettingsValue = <ValueId extends ValueIds >(valueId : ValueId ) =>
295+ useValue <ValueId >(valueId , STORE_ID );
296+ ```
297+
298+ This means that the rest of the app can use Values from the Store with correct
299+ types, _ and_ the implementation details of the Store are encapsulated in the
300+ single file. For more complex applications, it really helps to keep everything
301+ about the Store in one place.
302+
215303## Summary
216304
217305Schema-based typing provides a powerful developer-time experience for checking
0 commit comments