Skip to content

Commit 8550f60

Browse files
author
nebarf
committed
Allow to provide arbitrary user data carried by the http request
1 parent 670a35e commit 8550f60

File tree

8 files changed

+124
-1
lines changed

8 files changed

+124
-1
lines changed

README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Just follow links below to get an overview of library features.
4343
- [Abortable request return](#abortable-request-return)
4444
- [Example – Abortable request](#example--abortable-request)
4545
- [Example – Get request](#example--get-request)
46+
- [Example – Http context](#example--http-context)
4647
- [Request hooks](#request-hooks)
4748
- [Http request hook params](#http-request-hook-params)
4849
- [Http request hook return](#http-request-hook-return)
@@ -182,6 +183,7 @@ The complete *public API* exposed by the hook:
182183
| baseUrlOverride | string | The base url of the request. If provided, it would override the [provider](#provider) base url.
183184
| relativeUrl | string | The url relative to the base one (e.g. posts/1).
184185
| parser | [HttpResponseParser](src/client/types.ts) | An optional response parser that would override the [provider](#provider) global one. |
186+
| context | [HttpContext](src/client/http-context.ts) | An optional context that carries arbitrary user defined data. See examples.|
185187
| requestOptions | [HttpRequestOptions](./src/client/types.ts) | The options carried by the fetch request. |
186188

187189
### Request return
@@ -285,6 +287,49 @@ function App() {
285287
export default App;
286288
```
287289

290+
### Example – Http context
291+
```js
292+
import React, { useEffect } from 'react';
293+
import {
294+
useHttpClient,
295+
useHttpEvent,
296+
RequestStartedEvent,
297+
HttpContextToken,
298+
HttpContext, } from 'react-http-fetch';
299+
300+
const showGlobalLoader = new HttpContextToken(true);
301+
const reqContext = new HttpContext().set(showGlobalLoader, false);
302+
303+
function App() {
304+
const { request } = useHttpClient();
305+
306+
useHttpEvent(RequestStartedEvent, (payload) => {
307+
console.log('Show global loader:', payload.context.get(showGlobalLoader));
308+
});
309+
310+
useEffect(
311+
() => {
312+
const fetchTodo = async () => {
313+
await request({
314+
baseUrlOverride: 'https://jsonplaceholder.typicode.com',
315+
relativeUrl: 'todos/1',
316+
context: reqContext,
317+
});
318+
};
319+
320+
fetchTodo();
321+
},
322+
[request]
323+
);
324+
325+
return (
326+
<h1>Http Context</h1>
327+
);
328+
}
329+
330+
export default App;
331+
```
332+
288333
<br>
289334

290335
## Request hooks
@@ -296,6 +341,7 @@ The library provides a hook `useHttpRequest` managing the state of the http requ
296341
| baseUrlOverride | string | The base url of the request. If provided, it would override the [provider](#provider) base url.
297342
| relativeUrl | string | The url relative to the base one (e.g. posts/1).
298343
| parser | [HttpResponseParser](src/client/types.ts) | An optional response parser that would override the [provider](#provider) global one. |
344+
| context | [HttpContext](src/client/http-context.ts) | An optional context that carries arbitrary user defined data. See examples.|
299345
| requestOptions | [HttpRequestOptions](./src/client/types.ts) | The options carried by the fetch request. |
300346
| initialData | any | The value that the state assumes initially before the request is send. |
301347
| fetchOnBootstrap | boolean | Tell if the fetch must be triggered automatically when mounting the component or not. In the second case we would like to have a manual fetch, this is optained by a request function returned by the hook. |

src/client/http-context.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
export class HttpContextToken<ValueT> {
2+
constructor(public readonly defaultValue: ValueT) {}
3+
}
4+
5+
export class HttpContext {
6+
private readonly map = new Map<HttpContextToken<unknown>, unknown>();
7+
8+
/**
9+
* Store a value in the context. If a value is already present it will be overwritten.
10+
*/
11+
set<T>(token: HttpContextToken<T>, value: T): HttpContext {
12+
this.map.set(token, value);
13+
return this;
14+
}
15+
16+
/**
17+
* Retrieve the value associated with the given token.
18+
*/
19+
get<T>(token: HttpContextToken<T>): T | undefined {
20+
if (!this.map.has(token)) {
21+
this.map.set(token, token.defaultValue);
22+
}
23+
return this.map.get(token) as T;
24+
}
25+
26+
/**
27+
* Delete the value associated with the given token.
28+
*/
29+
delete(token: HttpContextToken<unknown>): HttpContext {
30+
this.map.delete(token);
31+
return this;
32+
}
33+
34+
/**
35+
* Checks for existence of a given token.
36+
*/
37+
has(token: HttpContextToken<unknown>): boolean {
38+
return this.map.has(token);
39+
}
40+
41+
/**
42+
* @returns a list of tokens currently stored in the context.
43+
*/
44+
keys(): IterableIterator<HttpContextToken<unknown>> {
45+
return this.map.keys();
46+
}
47+
}

src/client/http-request.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { HttpMethod } from '../enum';
2+
import { HttpContext, HttpContextToken } from './http-context';
23

34
export interface HttpRequestProps<HttpRequestBodyT> {
45
baseUrl: string;
@@ -10,6 +11,7 @@ export interface HttpRequestProps<HttpRequestBodyT> {
1011
queryParams?: Record<string, string>;
1112
relativeUrl: string;
1213
signal?: AbortSignal;
14+
context?: HttpContext;
1315
}
1416

1517
export class HttpRequest<HttpRequestBodyT> implements HttpRequestProps<HttpRequestBodyT> {
@@ -60,6 +62,11 @@ export class HttpRequest<HttpRequestBodyT> implements HttpRequestProps<HttpReque
6062
*/
6163
private _signal?: AbortSignal;
6264

65+
/**
66+
* The request context storing arbitrary user defined data.
67+
*/
68+
private _context?: HttpContext;
69+
6370
constructor(requestOpts: HttpRequestProps<HttpRequestBodyT>) {
6471
const {
6572
baseUrl,
@@ -71,6 +78,7 @@ export class HttpRequest<HttpRequestBodyT> implements HttpRequestProps<HttpReque
7178
queryParams,
7279
relativeUrl,
7380
signal,
81+
context,
7482
} = requestOpts;
7583

7684
this._baseUrl = baseUrl;
@@ -82,6 +90,7 @@ export class HttpRequest<HttpRequestBodyT> implements HttpRequestProps<HttpReque
8290
this._queryParams = queryParams;
8391
this._relativeUrl = relativeUrl;
8492
this._signal = signal;
93+
this._context = context;
8594
}
8695

8796
get baseUrl(): string {
@@ -138,4 +147,14 @@ export class HttpRequest<HttpRequestBodyT> implements HttpRequestProps<HttpReque
138147
}
139148
return `${this.url}?${this.serializedQueryParams}`;
140149
}
150+
151+
get context(): HttpContext | undefined {
152+
return this._context;
153+
}
154+
155+
getContextValue<ContextTokenValueT>(
156+
token: HttpContextToken<ContextTokenValueT>
157+
): ContextTokenValueT | undefined {
158+
return this.context ? this.context.get(token) : undefined;
159+
}
141160
}

src/client/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './use-http-client';
22
export * from './types';
33
export * from './http-request';
4+
export * from './http-context';

src/client/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { HttpMethod } from '../enum';
2+
import { HttpContext } from './http-context';
23

34
export interface UseHttpClientParams {
45
baseUrl: string;
@@ -13,6 +14,7 @@ export interface PerformHttpRequestParams<HttpRequestBodyT> {
1314
relativeUrl: string;
1415
parser: HttpResponseParser;
1516
baseUrlOverride: string;
17+
context: HttpContext;
1618
requestOptions: Partial<HttpRequestOptions<HttpRequestBodyT>>;
1719
}
1820

src/client/use-http-client.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export const useHttpClient = (): UseHttpClientReturn => {
3232
baseUrlOverride,
3333
parser,
3434
relativeUrl,
35+
context,
3536
requestOptions,
3637
}: Partial<PerformHttpRequestParams<HttpRequestBodyT>>): Promise<HttpResponseT> => {
3738
/**
@@ -69,6 +70,7 @@ export const useHttpClient = (): UseHttpClientReturn => {
6970
body: body || undefined,
7071
baseUrl: computedBaseUrl,
7172
relativeUrl: relativeUrl || '',
73+
context,
7274
});
7375

7476
/**
@@ -157,6 +159,7 @@ export const useHttpClient = (): UseHttpClientReturn => {
157159
parser,
158160
relativeUrl,
159161
requestOptions,
162+
context,
160163
}: Partial<
161164
PerformHttpRequestParams<HttpRequestBodyT>
162165
>): AbortableHttpRequestReturn<HttpResponseT> => {
@@ -167,6 +170,7 @@ export const useHttpClient = (): UseHttpClientReturn => {
167170
baseUrlOverride,
168171
parser,
169172
relativeUrl,
173+
context,
170174
requestOptions: {
171175
...requestOptions,
172176
signal,

src/request/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { HttpRequestOptions, HttpResponseParser } from '../client';
2+
import { HttpContext } from '../client/http-context';
23
import { HttpRequestState } from './state-reducer';
34

45
export interface UseHttpRequestParams<InitialDataT, HttpRequestBodyT> {
@@ -8,6 +9,7 @@ export interface UseHttpRequestParams<InitialDataT, HttpRequestBodyT> {
89
requestOptions: Partial<HttpRequestOptions<HttpRequestBodyT>>;
910
initialData: InitialDataT;
1011
fetchOnBootstrap: boolean;
12+
context: HttpContext;
1113
}
1214

1315
export interface UseHttpAbortableRequestReturn<HttpResponseT> {

src/request/use-http-request.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export const useHttpRequest = <HttpResponseT, HttpRequestBodyT = unknown>(
4646
parser: params.parser,
4747
relativeUrl: params.relativeUrl,
4848
requestOptions: params.requestOptions,
49+
context: params.context,
4950
}),
5051
[params],
5152
fastCompare
@@ -59,12 +60,13 @@ export const useHttpRequest = <HttpResponseT, HttpRequestBodyT = unknown>(
5960
source: Partial<PerformHttpRequestParams<HttpRequestBodyT>>,
6061
override: Partial<PerformHttpRequestParams<HttpRequestBodyT>>
6162
): Partial<PerformHttpRequestParams<HttpRequestBodyT>> => {
62-
const { baseUrlOverride, parser, relativeUrl, requestOptions } = override;
63+
const { baseUrlOverride, parser, relativeUrl, requestOptions, context } = override;
6364

6465
return {
6566
baseUrlOverride: baseUrlOverride || source.baseUrlOverride,
6667
parser: parser || source.parser,
6768
relativeUrl: relativeUrl || source.relativeUrl,
69+
context: context || source.context,
6870
requestOptions: {
6971
body: requestOptions?.body || source.requestOptions?.body,
7072
credentials: requestOptions?.credentials || source.requestOptions?.credentials,

0 commit comments

Comments
 (0)