Skip to content

Commit 2fde198

Browse files
committed
docs(store): rewrite readme and all of docs
1 parent 48845c4 commit 2fde198

12 files changed

+883
-199
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ dist-ssr
1111
server/dist
1212
public/dist
1313
.turbo
14+
coverage

apps/docs/pages/store/_meta.json

+9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22
"overview": {
33
"title": "Overview"
44
},
5+
"store-anatomy": {},
6+
"defining-stores": {},
7+
"reading-state": {},
8+
"updating-state": {},
9+
"onChange-and-effects": {},
10+
"local-state-management": {},
11+
"options-and-middleware": {},
12+
"extensions": {},
13+
"async-usage": {},
514
"todo-example": {
615
"title": "Todo Example",
716
"theme": {

apps/docs/pages/store/async-usage.mdx

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Async usage
2+
3+
Davstack store can be combined with davstack service to manage async state. Here is an example of how to use davstack store with davstack service to manage async state.
4+
5+
1. Export the trpc react query api utils from your trpc provider file
6+
7+
```tsx
8+
export let apiUtils = null as unknown as ReturnType<typeof api.useUtils>;
9+
10+
function InitApiClient() {
11+
const actualApiUtils = api.useUtils();
12+
13+
useEffect(() => {
14+
apiUtils = actualApiUtils;
15+
}, [actualApiUtils]);
16+
17+
return null;
18+
}
19+
20+
export function TRPCReactProvider(props: { children: React.ReactNode }) {
21+
// ... other code
22+
return (
23+
<api.Provider client={trpcClient} queryClient={queryClient}>
24+
<QueryClientProvider client={queryClient}>
25+
{props.children}
26+
<InitApiClient />
27+
</QueryClientProvider>
28+
</api.Provider>
29+
);
30+
}
31+
```
32+
33+
This allows you to access the api utils from anywhere in your app.
34+
35+
2. Create a store that manages the async state
36+
37+
```tsx
38+
import { store } from '@davstack/store';
39+
40+
export const notificationsStore = store({
41+
subscription: null as PushSubscription | null,
42+
registration: null as ServiceWorkerRegistration | null,
43+
})
44+
.computed((store) => ({
45+
isSubscribed: () => Boolean(store.subscription),
46+
}))
47+
.extend((store) => {
48+
async function init() {
49+
const registration = await navigator.serviceWorker.ready;
50+
51+
try {
52+
checkPushNotificationIsSupported();
53+
54+
const subscription = await registration.pushManager.getSubscription();
55+
56+
// use the api utils to make a request to the server
57+
await apiUtils.notification.checkSubscription.fetch({
58+
endpoint: subscription.endpoint,
59+
});
60+
61+
store.subscription.set(subscription);
62+
} catch (error) {
63+
console.error('Error initializing subscription:', error);
64+
}
65+
}
66+
67+
return {
68+
/**
69+
* Initializes the store, should only be place once in root layout
70+
*/
71+
Init() {
72+
useEffect(() => {
73+
init();
74+
}, []);
75+
76+
return null;
77+
},
78+
};
79+
});
80+
```
+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { Callout } from 'nextra/components';
2+
3+
# Defining Stores
4+
5+
To create a store, you can use the `store()` function and pass in the initial state:
6+
7+
```tsx
8+
import { store } from '@davstack/store';
9+
10+
const counterStore = store(0);
11+
```
12+
13+
Alternatively, you can use `store.state()` to define the initial state:
14+
15+
```tsx
16+
const counterStore = store().state(0);
17+
```
18+
19+
<Callout type="info" emoji="ℹ️">
20+
`store(initialState)` and `store.state(initialState)` are equivalent and can be used interchangeably.
21+
22+
</Callout>
23+
24+
## Defining Actions
25+
26+
Actions are functions that modify the store's state. They can be defined using the `actions` method:
27+
28+
```tsx
29+
const userStore = store()
30+
.state({
31+
name: 'John',
32+
age: 25,
33+
})
34+
.actions((store) => ({
35+
incrementAge() {
36+
store.age.set(store.age.get() + 1);
37+
},
38+
}));
39+
40+
// calling actions
41+
userStore.incrementAge();
42+
```
43+
44+
## Defining Computed Properties
45+
46+
Computed properties are derived values that are automatically updated when the store's state changes. They can be defined using the `computed` method:
47+
48+
```tsx
49+
const userStore = store()
50+
.state({
51+
name: 'John',
52+
age: 25,
53+
})
54+
.computed((store) => ({
55+
fullName: () => `${store.name.get()} Doe`,
56+
}));
57+
58+
// accessing computed properties
59+
const fullName = userStore.fullName.get();
60+
```
61+
62+
<Callout type="info" emoji="ℹ️">
63+
How do computed properties work?
64+
</Callout>
65+
66+
- The "store" passed into the computed callback is a proxy.
67+
- When you call `store.computedValue.get()`, then the store will be passed to the computed function like normal.
68+
- When you call `store.computedValue.use()`, then the store passed to computed function will detect any `store.get()` calls inside the computed function, and will replace them with `store.use()` calls.
69+
- This means that the value will be re-evaluated whenever any of the dependencies change, but it does not currently cache the result.

apps/docs/pages/store/extensions.mdx

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Extensions
2+
3+
Extensions allow you to assign any additional properties to the store, while keeping the store definition self-contained and reusable.
4+
5+
Under the hood, `actions`, `computed` and `effects` all wrap around the `extend` method.
6+
7+
However, you can also use `extend` directly to add any custom properties to the store, which don't direclty fit into the state, actions or computed properties.
8+
9+
## Basic usage example
10+
11+
```tsx
12+
import { store } from '@davstack/store';
13+
14+
const userStore = store()
15+
.state({
16+
name: 'John',
17+
age: 25,
18+
})
19+
.extend((store) => ({
20+
isAdmin: false,
21+
}));
22+
23+
// accessing the extension
24+
25+
const isAdmin = userStore.isAdmin.get();
26+
```
27+
28+
## Example usage with hooks
29+
30+
```tsx
31+
32+
const altStore = store({
33+
searchTerm: '',
34+
}).extend((store) => ({
35+
useFilteredBooks: () => {
36+
const searchTerm = store.searchTerm.use();
37+
// use react query or any other data fetching library here
38+
39+
},
40+
41+
```
42+
43+
## Example usage with components
44+
45+
```tsx
46+
import { store } from '@davstack/store';
47+
48+
export const notificationsStore = store({
49+
subscription: null as PushSubscription | null,
50+
registration: null as ServiceWorkerRegistration | null,
51+
}).
52+
.computed((store) => ({
53+
isSubscribed: () => Boolean(store.subscription),
54+
}))
55+
.extend((store) => {
56+
async function init() {
57+
const registration = await navigator.serviceWorker.ready;
58+
59+
try {
60+
checkPushNotificationIsSupported();
61+
62+
const subscription = await registration.pushManager.getSubscription();
63+
64+
if (!subscription) {
65+
console.log("No subscription found");
66+
return;
67+
}
68+
69+
70+
await apiUtils!.notification.checkSubscription.fetch({
71+
endpoint: subscription.endpoint,
72+
});
73+
74+
store.subscription.set(subscription);
75+
} catch (error) {
76+
console.error("Error initializing subscription:", error);
77+
}
78+
}
79+
80+
return {
81+
/**
82+
* Initializes the store, should only be place once in root layout
83+
*/
84+
Init() {
85+
useEffect(() => {
86+
init();
87+
}, []);
88+
89+
return null;
90+
}
91+
};
92+
});
93+
94+
// app/layout.tsx
95+
import { notificationsStore } from './notificationsStore';
96+
97+
export default function Layout({ children }:{children: React.ReactNode}) {
98+
return (
99+
<div>
100+
{children}
101+
<notificationsStore.Init>
102+
</div>
103+
);
104+
}
105+
106+
107+
```

0 commit comments

Comments
 (0)