Skip to content

Commit 294f253

Browse files
committed
Add DataState info to README
1 parent 8202145 commit 294f253

File tree

2 files changed

+71
-3
lines changed

2 files changed

+71
-3
lines changed

README.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,70 @@ This project was started as a Next.js application with server-side rendering hos
6262
* If after all that you manage to get things to compile, you might notice you don't get **live data** everywhere, some pages simply don't update on reload... This is because those pages haven't been converted to client components yet! **When a server component gets compiled as static site, all server-side data fetching gets baked into the output!** This means you can refresh as much as you want, the data will always remain the same as what it was when the project was built. So, the solution?
6363
**Convert everything to client components!**
6464

65+
## DataState Library
66+
Since this project is handling a lot of async data that has to be fetched from API providers, I co-developed the DataState library to make all of this simpler, more intuitive, and without the usual bloat that comes with client-side data fetching.
67+
68+
The library mainly consists of a `DataState` Algebraic Data Type, which always is in either of 3 states:
69+
- `LoadingState`: The data is still being fetched.
70+
- `ErrorState`: The fetching process ran into some kind of error. The `error` field contains the `Error` object.
71+
- `ValueState`: The fetch succeeded, and the resulting data is contained inside the `value` field of the DataState.
72+
73+
More specifically, the DataState consists of a `Root`, which contains the actual data fields, and `DataStateMethods` that extend the `Root` object into a full `DataState`.
74+
75+
Here are the `Root` definitions for reference:
76+
```ts
77+
export type LoadingRoot = {
78+
status: 'loading';
79+
value: undefined;
80+
error: undefined | null;
81+
loading: true;
82+
};
83+
export type ValueRoot<T> = {
84+
status: 'value';
85+
value: T;
86+
error: undefined;
87+
loading: false;
88+
};
89+
export type ErrorRoot = {
90+
status: 'error';
91+
value: undefined;
92+
error: Error;
93+
loading: false;
94+
};
95+
96+
export type Root<T> = LoadingRoot | ValueRoot<T> | ErrorRoot;
97+
```
98+
99+
### Usage
100+
A `DataState` is initialized with `useDataState()`, which takes a fetching function and its arguments as input. E.g.:
101+
```ts
102+
const blockData = useDataState<Block>({
103+
fetcher: (alchemy, num) => alchemy.core.getBlock(num),
104+
args: [alchemy, blockNumber],
105+
});
106+
```
107+
Once initialized, it can be used directly inside the JSX return statement of the component, e.g.:
108+
```tsx
109+
return (
110+
<div>
111+
<span>Recipient of block reward:</span>
112+
<blockData.Render
113+
className='w-[11rem]'
114+
loadingPulseColor='bg-(--link-color)'
115+
>
116+
{
117+
(data) =>
118+
<PopoverLink
119+
href={`/${props.network}/address?hash=${data.miner}`}
120+
content={truncateAddress(data.miner, 20)}
121+
popover={data.miner}
122+
className='left-[-37%] top-[-2.6rem] w-78 py-1.5 px-2.5'
123+
/>
124+
}
125+
</blockData.Render>
126+
</div>
127+
);
128+
```
129+
As shown above, every DataState has a `.Render()` function that receives the fetched data as input, allowing the use of the fetched data directly inside the JSX code:
130+
* Obviously, the Render function only renders the actual data when it is present. If not present, it will render either an `ErrorIndicator` indicating the error that occurred, or a `LoadingIndicator` or `LoadingPulse` component when the data is still being fetched.
131+
* All of this is very configurable; see the `RenderConfig` type in `src/lib/data-state/types/index.ts` on line 107. The `RenderConfig` only consists of optional fields to configure the `Render()` function's output. This means the `<blockData.Render>` component's props are all optional, and you can pick and choose only the ones that you need.

src/lib/data-state/types/index.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,16 +130,17 @@ export type RenderConfig<T, K extends keyof T> = {
130130
// (`showFallback` takes precedence over both showLoading and showError!)
131131
showFallback?: boolean;
132132

133-
// Optional message to display while loading:
133+
// Optionally display a LoadingIndicator with the following message:
134134
loadingMessage?: string;
135135
// Optional className for just the LoadingPulse component, e.g. for setting its color,
136136
// which it takes from currentcolor (i.e. text color) by default:
137137
// (The LoadingPulse component will only display when the above
138-
// loadingMessage is NOT set, otherwise only the message will appear.)
138+
// loadingMessage is NOT set, otherwise only the LoadingIndicator with message will appear.)
139139
loadingPulseColor?: string;
140140
// Optionally display a fallback component while loading:
141141
// (True by default, so you only need to provide the `loadingCallback()`
142-
// function below to actually display it.)
142+
// function below to actually display it. When false, the loadingCallback
143+
// below will be ignored!)
143144
showLoadingCallback?: boolean;
144145
// Optionally display another component instead of the default LoadingIndicator:
145146
// (Can optionally use the `className` below, just like `children()`.)

0 commit comments

Comments
 (0)