Skip to content

Commit b98a6d4

Browse files
committed
Remove DataState postProcessing() function in favour of renamed useSubset()
1 parent 3b5f25a commit b98a6d4

File tree

6 files changed

+62
-59
lines changed

6 files changed

+62
-59
lines changed

src/components/content/home-page/blocks/block.tsx

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,17 @@ export default function Block(props: {
2222
const alchemy = useAlchemy(props.network);
2323
const blockNumber = props.latestBlockData.value && props.latestBlockData.value - props.id;
2424

25-
const blockData = useDataState<BlockP, [Alchemy, number?], Block>({
25+
const blockData = useDataState<Block>({
2626
fetcher: (alchemy, num) => alchemy.core.getBlock(num!),
2727
args: [alchemy, blockNumber],
28-
postProcess: (response) => ({
28+
}).useTransform(
29+
(response) => ({
2930
timestamp: `(${getBlockAgeFromSecs(getSecsFromUnixSecs(response.timestamp))} ago)`,
3031
transactions: `${response.transactions.length} transactions`,
3132
recipientHashFull: response.miner,
3233
recipientHashShort: truncateAddress(response.miner, 20),
33-
}),
34-
});
34+
})
35+
);
3536

3637
return (
3738
<div className='min-h-[8.5rem] md:min-h-[5.8rem] p-2 md:p-3 border-b border-(--border-color) last:border-0'>
@@ -84,14 +85,14 @@ export default function Block(props: {
8485
className='w-[11rem]'
8586
loadingPulseColor='bg-(--link-color)'
8687
>
87-
{
88-
({ recipientHashFull, recipientHashShort }) =>
89-
<PopoverLink
90-
href={`/${props.network}/address?hash=${recipientHashFull}`}
91-
content={recipientHashShort}
92-
popover={recipientHashFull}
93-
className='left-[-37%] top-[-2.6rem] w-78 py-1.5 px-2.5'
94-
/>
88+
{
89+
({ recipientHashFull, recipientHashShort }) =>
90+
<PopoverLink
91+
href={`/${props.network}/address?hash=${recipientHashFull}`}
92+
content={recipientHashShort}
93+
popover={recipientHashFull}
94+
className='left-[-37%] top-[-2.6rem] w-78 py-1.5 px-2.5'
95+
/>
9596
}
9697
</blockData.Render>
9798
</span>

src/components/content/home-page/transactions/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default function Transactions(props: {
1313
}) {
1414
const alchemy = useAlchemy(props.network);
1515

16-
const blockWithTransactionsData = useDataState<BlockWithTransactions, [Alchemy, number?]>({
16+
const blockWithTransactionsData = useDataState<BlockWithTransactions>({
1717
fetcher: (alchemy, num) => alchemy.core.getBlockWithTransactions(num!),
1818
args: [alchemy, props.latestBlockData.value],
1919
});

src/components/content/home-page/transactions/transaction.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export default function Transaction(props: {
1212
network: string,
1313
blockWithTransactionsData: DataState<BlockWithTransactions>,
1414
}) {
15-
const txData = props.blockWithTransactionsData.useSubset(
15+
const txData = props.blockWithTransactionsData.useTransform(
1616
(data, id) => ({ ...data.transactions[id] }),
1717
[props.id]
1818
);

src/lib/data-state/constructors/data-state.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Constructors for the DataState type:
2020
// Create a `DataState<T>` type from a FetchConfig object, and initialize it as a LoadingRoot.
2121
// This function needs to create all DataStateMethods to pass on to the DataState!
2222
// Contrary to `useDataState()`, this constructor does NOT actually run the fetch!
23-
export const useConfig: DataStateConstructor = <T, A extends any[], R>(config: FetchConfig<T, A, R>) => {
23+
export const useConfig: DataStateConstructor = <T, A extends any[] = any[]>(config: FetchConfig<T, A>) => {
2424
// Initialize a LoadingRoot as root variant for the DataState:
2525
// Calling `DataState.loading()` inside `useState()` is required to
2626
// get a LoadingRoot (DataRoot<undefined>) instead of an `undefined`.

src/lib/data-state/methods.tsx

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ import { RenderError } from './types/errors';
1010
import { createLoadingRoot, createValueRoot, createErrorRoot } from './constructors/data-root';
1111

1212

13-
export function useDataStateMethods<T, A extends any[], R>(
13+
export function useDataStateMethods<T, A extends any[] = any[]>(
1414
dataRoot: DataRoot<T>,
1515
setDataRoot: Dispatch<SetStateAction<DataRoot<T>>>,
16-
config: FetchConfig<T, A, R>
16+
config: FetchConfig<T, A>
1717
) {
1818
const setLoading = () => setDataRoot(createLoadingRoot());
1919
const setValue = (dataValue: T) => setDataRoot(createValueRoot(dataValue));
@@ -31,25 +31,22 @@ export function useDataStateMethods<T, A extends any[], R>(
3131
// it always KEEPS the FIRST VERSION that it received!
3232
// Those variables have to be provided as ARGUMENTS!
3333
const fetcher = useRef(config.fetcher).current;
34-
const postProcess = useRef(config.postProcess).current;
3534

3635
// Attach setDataRoot to fetcher so fetching can update the DataState:
3736
const fetch = useCallback(async () => {
3837
try {
3938
// Skip fetching if one of the arguments is still undefined:
4039
if (args?.some(arg => arg === undefined)) return;
4140

42-
let response: R;
41+
let response: T;
4342
if (fetcher) response = await fetcher(...args);
4443
else response = await fetchJson(args[0] as string);
4544

46-
const result = (postProcess ? postProcess(response) : response) as T;
47-
48-
setDataRoot(createValueRoot(result));
45+
setDataRoot(createValueRoot(response));
4946
} catch (err) {
5047
setDataRoot(createErrorRoot(err));
5148
}
52-
}, [fetcher, args, postProcess, setDataRoot]);
49+
}, [fetcher, args, setDataRoot]);
5350

5451
// Get an actual value by doing initial fetch in useEffect():
5552
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -109,38 +106,32 @@ export function useDataStateMethods<T, A extends any[], R>(
109106
}
110107

111108
// Create a new DataState containing a subset of the fields of another:
112-
const useSubset = <S, B extends any[]>(
113-
selectorFn: (data: T, ...args: B) => S,
114-
selectorArgs?: B,
115-
): DataState<S> => {
109+
const useTransform = <U, B extends any[]>(
110+
transformer: (data: T, ...args: B) => U,
111+
transformerArgs?: B,
112+
): DataState<U> => {
116113
// Stabilize the selector function once - it never changes:
117-
const stableSelector = useRef(selectorFn).current;
114+
const stableTransformer = useRef(transformer).current;
118115
// Stabilize the arguments:
119116
// eslint-disable-next-line react-hooks/exhaustive-deps
120-
const stableArgs = useMemo(() => selectorArgs || [] as unknown as B, selectorArgs || [] as unknown as B);
121-
122-
const subsetPostProcess = useCallback((response: R): S => {
123-
const result = (postProcess ? postProcess(response) : response) as unknown as T;
124-
return stableSelector(result, ...stableArgs);
125-
}, [stableArgs, stableSelector]);
117+
const stableArgs = useMemo(() => transformerArgs || [] as unknown as B, transformerArgs || [] as unknown as B);
126118

127-
const subsetConfig = {
128-
fetcher: fetcher,
129-
args: args,
130-
postProcess: subsetPostProcess,
119+
const transformConfig = {
120+
transformer: stableTransformer,
121+
args: stableArgs,
131122
};
132123

133124
// Construct the new subset DataState to return:
134-
const subsetData = useConfig<S, A, R>(subsetConfig);
125+
const subsetData = useConfig<U, A>(transformConfig);
135126

136127
useEffect(() => {
137128
switch (dataRoot.status) {
138129
case 'loading':
139130
break;
140131
case 'value':
141132
try {
142-
const selectedValue = stableSelector(dataRoot.value, ...stableArgs);
143-
subsetData.setValue(selectedValue);
133+
const transformedValue = stableTransformer(dataRoot.value, ...stableArgs);
134+
subsetData.setValue(transformedValue);
144135
} catch (err) {
145136
// Handle selector errors gracefully:
146137
subsetData.setError(
@@ -160,5 +151,5 @@ export function useDataStateMethods<T, A extends any[], R>(
160151
return subsetData;
161152
}
162153

163-
return { setLoading, setValue, setError, fetch, useInit, Render, useSubset };
154+
return { setLoading, setValue, setError, fetch, useInit, Render, useTransform };
164155
}

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

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { DependencyList, Dispatch, ReactNode, SetStateAction } from 'react';
1+
import { ReactNode } from 'react';
22

33

44
export type LoadingRoot = {
@@ -26,12 +26,6 @@ export type LoadingRootConstructor = <T,>() => DataRoot<T>;
2626
export type ValueRootConstructor = <T,>(dataValue: T) => DataRoot<T>;
2727
export type ErrorRootConstructor = <T,>(unknownError: unknown, errorPrefix?: string) => DataRoot<T>;
2828

29-
// Function to transform an object by selecting/transforming the input object
30-
// and returning a modified object containing only the selected fields.
31-
// S is the type of the resulting selection, I is the input type, and A are the
32-
// OPTIONAL arguments from outside the caller to be used for transforming the data:
33-
type Selector<S, I, A extends any[] = []> = (input: I, ...args: A) => S;
34-
3529
// DataState Methods that extend the DataRoot<T> into a full DataState<T> type:
3630
export type DataStateMethods<T> = {
3731
// These methods set the DataRoot value using React's useState.
@@ -53,10 +47,10 @@ export type DataStateMethods<T> = {
5347
// and render that, or otherwise default to rendering the DataState's value directly.
5448
Render: <K extends keyof T>(options?: RenderConfig<T, K>) => ReactNode;
5549
// Create a new DataState containing a subset of the fields of another:
56-
useSubset: <S, A extends any[]>(
57-
selector: Selector<S, T, A>,
50+
useTransform: <U, A extends any[]>(
51+
transformer: Transformer<U, T, A>,
5852
args?: A,
59-
) => DataState<S>;
53+
) => DataState<U>;
6054
// Create a new DataState by composing the values from 2 different DataStates:
6155
// (E.g. When some data transformation requires data from 2 different fetches)
6256
// useCompose: <U, C>(
@@ -132,17 +126,34 @@ export type DataState<T> = DataRoot<T> & DataStateMethods<T>;
132126

133127
// FetchConfig is used to initialize a DataState<T>, which is a DataRoot<T>,
134128
// together with its fetching function for potentially refetching it:
135-
export type FetchConfig<T, A extends any[] = any[], R = T> = {
129+
export type FetchConfig<T, A extends any[] = []> = {
136130
// `fetcher` takes any async function, and can be omitted
137131
// if the fetcher is the standard Fetch API:
138-
fetcher?: (...args: A) => Promise<R>; // R is the raw Response type!
132+
fetcher?: (...args: A) => Promise<T>;
139133
// Optionally provide arguments for the fetcher:
140134
args?: A;
141-
// Optional postprocessing function to transform fetched data from
142-
// raw Response type R to DataState type T before creating DataState<T>:
143-
postProcess?: Selector<T, R>;
144135
};
145136

146-
export type DataStateConstructor = <T, A extends any[] = any[], R = T>(
147-
config: FetchConfig<T, A, R>
137+
// Function to transform an object by selecting/transforming the input object
138+
// and returning a modified object containing only the selected fields.
139+
// S is the type of the resulting selection, I is the input type, and A are the
140+
// OPTIONAL arguments from outside the caller to be used for transforming the data:
141+
type Transformer<T, I, A extends any[] = any[]> = (input: I, ...args: A) => T;
142+
143+
// Config for selecting/transforming an input object and returning
144+
// a new, modified object containing only the selected/transformed fields.
145+
// T is the type of the resulting transformation, I is the input type,
146+
// and A are the OPTIONAL arguments from outside the caller to be used
147+
// for transforming the data:
148+
export type TransformConfig<T, I, A extends any[] = any[]> = {
149+
// Transformer does the actual transformation by returning a selection of fields:
150+
transformer: Transformer<T, I, A>;
151+
// Optionally provide arguments for the transformer:
152+
args?: A;
153+
};
154+
155+
// A DataState is either initialized from a fetcher function,
156+
// or transformed from an input object:
157+
export type DataStateConstructor = <T, I = T, A extends any[] = any[]>(
158+
config: FetchConfig<T, A> | TransformConfig<T, I, A>
148159
) => DataState<T>;

0 commit comments

Comments
 (0)