···11+Welcome to your new TanStack app!
22+33+# Getting Started
44+55+To run this application:
66+77+```bash
88+npm install
99+npm run start
1010+```
1111+1212+# Building For Production
1313+1414+To build this application for production:
1515+1616+```bash
1717+npm run build
1818+```
1919+2020+## Testing
2121+2222+This project uses [Vitest](https://vitest.dev/) for testing. You can run the tests with:
2323+2424+```bash
2525+npm run test
2626+```
2727+2828+## Styling
2929+3030+This project uses CSS for styling.
3131+3232+3333+3434+3535+## Routing
3636+This project uses [TanStack Router](https://tanstack.com/router). The initial setup is a file based router. Which means that the routes are managed as files in `src/routes`.
3737+3838+### Adding A Route
3939+4040+To add a new route to your application just add another a new file in the `./src/routes` directory.
4141+4242+TanStack will automatically generate the content of the route file for you.
4343+4444+Now that you have two routes you can use a `Link` component to navigate between them.
4545+4646+### Adding Links
4747+4848+To use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/react-router`.
4949+5050+```tsx
5151+import { Link } from "@tanstack/react-router";
5252+```
5353+5454+Then anywhere in your JSX you can use it like so:
5555+5656+```tsx
5757+<Link to="/about">About</Link>
5858+```
5959+6060+This will create a link that will navigate to the `/about` route.
6161+6262+More information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/react/api/router/linkComponent).
6363+6464+### Using A Layout
6565+6666+In the File Based Routing setup the layout is located in `src/routes/__root.tsx`. Anything you add to the root route will appear in all the routes. The route content will appear in the JSX where you use the `<Outlet />` component.
6767+6868+Here is an example layout that includes a header:
6969+7070+```tsx
7171+import { Outlet, createRootRoute } from '@tanstack/react-router'
7272+import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
7373+7474+import { Link } from "@tanstack/react-router";
7575+7676+export const Route = createRootRoute({
7777+ component: () => (
7878+ <>
7979+ <header>
8080+ <nav>
8181+ <Link to="/">Home</Link>
8282+ <Link to="/about">About</Link>
8383+ </nav>
8484+ </header>
8585+ <Outlet />
8686+ <TanStackRouterDevtools />
8787+ </>
8888+ ),
8989+})
9090+```
9191+9292+The `<TanStackRouterDevtools />` component is not required so you can remove it if you don't want it in your layout.
9393+9494+More information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts).
9595+9696+9797+## Data Fetching
9898+9999+There are multiple ways to fetch data in your application. You can use TanStack Query to fetch data from a server. But you can also use the `loader` functionality built into TanStack Router to load the data for a route before it's rendered.
100100+101101+For example:
102102+103103+```tsx
104104+const peopleRoute = createRoute({
105105+ getParentRoute: () => rootRoute,
106106+ path: "/people",
107107+ loader: async () => {
108108+ const response = await fetch("https://swapi.dev/api/people");
109109+ return response.json() as Promise<{
110110+ results: {
111111+ name: string;
112112+ }[];
113113+ }>;
114114+ },
115115+ component: () => {
116116+ const data = peopleRoute.useLoaderData();
117117+ return (
118118+ <ul>
119119+ {data.results.map((person) => (
120120+ <li key={person.name}>{person.name}</li>
121121+ ))}
122122+ </ul>
123123+ );
124124+ },
125125+});
126126+```
127127+128128+Loaders simplify your data fetching logic dramatically. Check out more information in the [Loader documentation](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#loader-parameters).
129129+130130+### React-Query
131131+132132+React-Query is an excellent addition or alternative to route loading and integrating it into you application is a breeze.
133133+134134+First add your dependencies:
135135+136136+```bash
137137+npm install @tanstack/react-query @tanstack/react-query-devtools
138138+```
139139+140140+Next we'll need to create a query client and provider. We recommend putting those in `main.tsx`.
141141+142142+```tsx
143143+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
144144+145145+// ...
146146+147147+const queryClient = new QueryClient();
148148+149149+// ...
150150+151151+if (!rootElement.innerHTML) {
152152+ const root = ReactDOM.createRoot(rootElement);
153153+154154+ root.render(
155155+ <QueryClientProvider client={queryClient}>
156156+ <RouterProvider router={router} />
157157+ </QueryClientProvider>
158158+ );
159159+}
160160+```
161161+162162+You can also add TanStack Query Devtools to the root route (optional).
163163+164164+```tsx
165165+import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
166166+167167+const rootRoute = createRootRoute({
168168+ component: () => (
169169+ <>
170170+ <Outlet />
171171+ <ReactQueryDevtools buttonPosition="top-right" />
172172+ <TanStackRouterDevtools />
173173+ </>
174174+ ),
175175+});
176176+```
177177+178178+Now you can use `useQuery` to fetch your data.
179179+180180+```tsx
181181+import { useQuery } from "@tanstack/react-query";
182182+183183+import "./App.css";
184184+185185+function App() {
186186+ const { data } = useQuery({
187187+ queryKey: ["people"],
188188+ queryFn: () =>
189189+ fetch("https://swapi.dev/api/people")
190190+ .then((res) => res.json())
191191+ .then((data) => data.results as { name: string }[]),
192192+ initialData: [],
193193+ });
194194+195195+ return (
196196+ <div>
197197+ <ul>
198198+ {data.map((person) => (
199199+ <li key={person.name}>{person.name}</li>
200200+ ))}
201201+ </ul>
202202+ </div>
203203+ );
204204+}
205205+206206+export default App;
207207+```
208208+209209+You can find out everything you need to know on how to use React-Query in the [React-Query documentation](https://tanstack.com/query/latest/docs/framework/react/overview).
210210+211211+## State Management
212212+213213+Another common requirement for React applications is state management. There are many options for state management in React. TanStack Store provides a great starting point for your project.
214214+215215+First you need to add TanStack Store as a dependency:
216216+217217+```bash
218218+npm install @tanstack/store
219219+```
220220+221221+Now let's create a simple counter in the `src/App.tsx` file as a demonstration.
222222+223223+```tsx
224224+import { useStore } from "@tanstack/react-store";
225225+import { Store } from "@tanstack/store";
226226+import "./App.css";
227227+228228+const countStore = new Store(0);
229229+230230+function App() {
231231+ const count = useStore(countStore);
232232+ return (
233233+ <div>
234234+ <button onClick={() => countStore.setState((n) => n + 1)}>
235235+ Increment - {count}
236236+ </button>
237237+ </div>
238238+ );
239239+}
240240+241241+export default App;
242242+```
243243+244244+One of the many nice features of TanStack Store is the ability to derive state from other state. That derived state will update when the base state updates.
245245+246246+Let's check this out by doubling the count using derived state.
247247+248248+```tsx
249249+import { useStore } from "@tanstack/react-store";
250250+import { Store, Derived } from "@tanstack/store";
251251+import "./App.css";
252252+253253+const countStore = new Store(0);
254254+255255+const doubledStore = new Derived({
256256+ fn: () => countStore.state * 2,
257257+ deps: [countStore],
258258+});
259259+doubledStore.mount();
260260+261261+function App() {
262262+ const count = useStore(countStore);
263263+ const doubledCount = useStore(doubledStore);
264264+265265+ return (
266266+ <div>
267267+ <button onClick={() => countStore.setState((n) => n + 1)}>
268268+ Increment - {count}
269269+ </button>
270270+ <div>Doubled - {doubledCount}</div>
271271+ </div>
272272+ );
273273+}
274274+275275+export default App;
276276+```
277277+278278+We use the `Derived` class to create a new store that is derived from another store. The `Derived` class has a `mount` method that will start the derived store updating.
279279+280280+Once we've created the derived store we can use it in the `App` component just like we would any other store using the `useStore` hook.
281281+282282+You can find out everything you need to know on how to use TanStack Store in the [TanStack Store documentation](https://tanstack.com/store/latest).
283283+284284+# Demo files
285285+286286+Files prefixed with `demo` can be safely deleted. They are there to provide a starting point for you to play around with the features you've installed.
287287+288288+# Learn More
289289+290290+You can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com).
···11+import { atom } from 'jotai';
22+import { atomFamily } from 'jotai/utils';
33+import type { EsavDocument, QueryState, LogEntry } from './types';
44+const MAX_LOG_SIZE = 500;
55+66+/**
77+ * Manages the WebSocket instance itself.
88+ * Should only be written to by the provider.
99+ */
1010+export const websocketAtom = atom<WebSocket | null>(null);
1111+1212+/**
1313+ * Tracks the current status of the WebSocket connection.
1414+ */
1515+export const websocketStatusAtom = atom<'connecting' | 'open' | 'closed'>('closed');
1616+1717+/**
1818+ * A global, normalized cache for all documents received from the server.
1919+ * Maps a document URI (at://...) to its full data.
2020+ * This prevents data duplication across multiple queries.
2121+ */
2222+export const documentsAtom = atom<Record<string, EsavDocument>>({});
2323+2424+/**
2525+ * A family of atoms to hold the state for each individual query.
2626+ * You get the state for a query by providing its unique queryId.
2727+ */
2828+export const queryStateFamily = atomFamily((_queryId: string) =>
2929+ atom<QueryState | null>(null)
3030+);
3131+3232+/**
3333+ * Tracks active subscriptions and their component usage count.
3434+ * This is an internal atom used by our hooks to know when to
3535+ * send `subscribe` and `unsubscribe` messages.
3636+ */
3737+export const activeSubscriptionsAtom = atom<
3838+ Record<string, { count: number; esQuery: Record<string, any> }>
3939+>({});
4040+4141+4242+/**
4343+ * Holds the array of log entries for display.
4444+ */
4545+export const logEntriesAtom = atom<LogEntry[]>([]);
4646+4747+let logIdCounter = 0;
4848+4949+/**
5050+ * A "write-only" atom to add a new entry to the log.
5151+ * This encapsulates the logic for creating a new entry with an ID and timestamp.
5252+ * Any component can call this to add a log without needing to know the implementation details.
5353+ */
5454+export const addLogEntryAtom = atom(
5555+ null,
5656+ (get, set, newEntry: Omit<LogEntry, 'id' | 'timestamp'>) => {
5757+ const entry: LogEntry = {
5858+ id: logIdCounter++,
5959+ timestamp: new Date(),
6060+ ...newEntry,
6161+ };
6262+ const currentLog = get(logEntriesAtom);
6363+ const newLog = [entry, ...currentLog];
6464+ if (newLog.length > MAX_LOG_SIZE) {
6565+ newLog.length = MAX_LOG_SIZE;
6666+ }
6767+ set(logEntriesAtom, newLog);
6868+ }
6969+);