Merge branch 'next'

This commit is contained in:
Dmitriy Pleshevskiy 2020-12-23 00:28:45 +03:00
commit 7df0c94f3e
10 changed files with 60 additions and 66 deletions

25
package-lock.json generated
View file

@ -1,6 +1,6 @@
{ {
"name": "react-rest-request", "name": "react-rest-request",
"version": "0.4.1", "version": "0.5.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -5083,7 +5083,8 @@
"js-tokens": { "js-tokens": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
"dev": true
}, },
"js-yaml": { "js-yaml": {
"version": "3.13.1", "version": "3.13.1",
@ -5340,14 +5341,6 @@
"integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
"dev": true "dev": true
}, },
"loose-envify": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
"integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
"requires": {
"js-tokens": "^3.0.0"
}
},
"make-dir": { "make-dir": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
@ -5624,7 +5617,8 @@
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"dev": true
}, },
"object-copy": { "object-copy": {
"version": "0.1.0", "version": "0.1.0",
@ -6256,15 +6250,6 @@
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
"dev": true "dev": true
}, },
"react": {
"version": "17.0.1",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.1.tgz",
"integrity": "sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
},
"react-is": { "react-is": {
"version": "17.0.1", "version": "17.0.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz",

View file

@ -1,6 +1,6 @@
{ {
"name": "react-rest-request", "name": "react-rest-request",
"version": "0.4.1", "version": "0.5.3",
"description": "Minimalistic REST API client for React inspired by Apollo", "description": "Minimalistic REST API client for React inspired by Apollo",
"readme": "README.md", "readme": "README.md",
"main": "./target/index.js", "main": "./target/index.js",

View file

@ -8,13 +8,16 @@ export enum Method {
DELETE = 'DELETE', DELETE = 'DELETE',
} }
export type Endpoint<R, _V, P = never> = Readonly<{ export type Endpoint<R, V, P = unknown> = Readonly<{
_?: V; // Temporary hack to extract the type of variables. Do not use it in real endpoints.
method: Method; method: Method;
url: string | ((params: P) => string); url: string | ((params: P) => string);
headers?: Record<string, string>; headers?: Record<string, string>;
transformResponseData?: (data: any) => R; transformResponseData?: (data: any) => R;
}> }>
export type AnyEndpoint = Endpoint<any, any, any>
export type ExtractEndpointResponse<E> = E extends Endpoint<infer R, any, any> ? R : E extends Endpoint<infer R, any> ? R : never; export type ExtractEndpointResponse<E> = E extends Endpoint<infer R, any, any> ? R : E extends Endpoint<infer R, any> ? R : never;
export type ExtractEndpointVariables<E> = E extends Endpoint<any, infer V, any> ? V : E extends Endpoint<any, infer V> ? V : never; export type ExtractEndpointVariables<E> = E extends Endpoint<any, infer V, any> ? V : E extends Endpoint<any, infer V> ? V : never;
export type ExtractEndpointParams<E> = E extends Endpoint<any, any, infer P> ? P : never; export type ExtractEndpointParams<E> = E extends Endpoint<any, any, infer P> ? P : never;

View file

@ -2,13 +2,13 @@ import React from 'react';
import invariant from 'tiny-invariant'; import invariant from 'tiny-invariant';
import isEqual from 'lodash.isequal'; import isEqual from 'lodash.isequal';
import { useClient } from './client-hook'; import { useClient } from './client-hook';
import { Endpoint, ExtractEndpointParams, ExtractEndpointResponse, ExtractEndpointVariables } from './endpoint'; import { AnyEndpoint, ExtractEndpointParams, ExtractEndpointResponse, ExtractEndpointVariables } from './endpoint';
import { PublicRequestState, RequestAction, requestReducer, RequestState } from './reducer'; import { PublicRequestState, RequestReducer, requestReducer } from './reducer';
import { useRequestContext } from './request-context'; import { useRequestContext } from './request-context';
import { ClientResponse } from './client'; import { ClientResponse } from './client';
import { isFunction } from './misc'; import { isFunction } from './misc';
export type LazyRequestConfig<R, V, P = never> = Readonly<{ export type LazyRequestConfig<R, V, P> = Readonly<{
variables?: V; variables?: V;
params?: P; params?: P;
headers?: Record<string, string>; headers?: Record<string, string>;
@ -16,29 +16,33 @@ export type LazyRequestConfig<R, V, P = never> = Readonly<{
onFailure?: (res: ClientResponse<R>) => unknown; onFailure?: (res: ClientResponse<R>) => unknown;
}> }>
export type LazyRequestHandlerConfig<R, V, P> = Readonly< export type LazyRequestConfigFromEndpoint<E extends AnyEndpoint> = LazyRequestConfig<
LazyRequestConfig<R, V, P> ExtractEndpointResponse<E>,
ExtractEndpointVariables<E>,
ExtractEndpointParams<E>
>;
export type LazyRequestHandlerConfig<E extends AnyEndpoint> = Readonly<
LazyRequestConfigFromEndpoint<E>
& { force?: boolean } & { force?: boolean }
> >
export type RequestHandler<R, V, P> = (config?: LazyRequestHandlerConfig<R, V, P>) => Promise<R | null>; export type RequestHandler<E extends AnyEndpoint> =
(config?: LazyRequestHandlerConfig<E>) => Promise<ExtractEndpointResponse<E> | null>;
export type RefetchRequestHandler = () => void; export type RefetchRequestHandler = () => void;
export type PublicRequestStateWithRefetch<R> = PublicRequestState<R> & { refetch: RefetchRequestHandler }; export type PublicRequestStateWithRefetch<E extends AnyEndpoint> =
PublicRequestState<ExtractEndpointResponse<E>>
& { refetch: RefetchRequestHandler };
export function useLazyRequest< export function useLazyRequest<E extends AnyEndpoint>(
E extends Endpoint<R, V, P>,
R = ExtractEndpointResponse<E>,
V = ExtractEndpointVariables<E>,
P = ExtractEndpointParams<E>
>(
endpoint: E, endpoint: E,
config?: LazyRequestConfig<R, V, P>, config?: LazyRequestConfigFromEndpoint<E>,
): [RequestHandler<R, V, P>, PublicRequestStateWithRefetch<R>] { ): [RequestHandler<E>, PublicRequestStateWithRefetch<E>] {
const [client] = useClient(); const [client] = useClient();
const { defaultHeaders } = useRequestContext(); const { defaultHeaders } = useRequestContext();
const [state, dispatch] = React.useReducer<React.Reducer<RequestState<R>, RequestAction<R>>>( const [state, dispatch] = React.useReducer<RequestReducer<ExtractEndpointResponse<E>>>(
requestReducer, requestReducer,
{ {
data: null, data: null,
@ -46,24 +50,24 @@ export function useLazyRequest<
isCalled: false, isCalled: false,
} }
); );
const [prevHandlerConfig, setPrevHandlerConfig] = React.useState<LazyRequestHandlerConfig<R, V, P> | null>(null); const [prevHandlerConfig, setPrevHandlerConfig] = React.useState<LazyRequestHandlerConfig<E> | null>(null);
const transformResponseData = React.useCallback( const transformResponseData = React.useCallback(
(data: unknown): R => { (data: unknown): ExtractEndpointResponse<E> => {
return isFunction(endpoint.transformResponseData) ? return isFunction(endpoint.transformResponseData) ?
endpoint.transformResponseData(data) endpoint.transformResponseData(data)
: data as R; : data as ExtractEndpointResponse<E>;
}, },
[endpoint] [endpoint]
); );
const handler = React.useCallback( const handler = React.useCallback(
(handlerConfig?: LazyRequestHandlerConfig<R, V, P>) => { (handlerConfig?: LazyRequestHandlerConfig<E>) => {
if (state?.loading) { if (state?.loading) {
return Promise.resolve(null); return Promise.resolve(null);
} }
let params: P | undefined; let params: ExtractEndpointParams<E> | undefined;
let endpointUrl: string; let endpointUrl: string;
let isSameRequest = true; let isSameRequest = true;
if (isFunction(endpoint.url)) { if (isFunction(endpoint.url)) {
@ -106,7 +110,7 @@ export function useLazyRequest<
setPrevHandlerConfig(handlerConfig ?? {}); setPrevHandlerConfig(handlerConfig ?? {});
return client return client
.request<R>({ .request<ExtractEndpointResponse<E>>({
...endpoint, ...endpoint,
url: endpointUrl, url: endpointUrl,
headers, headers,

View file

@ -28,6 +28,8 @@ export type RequestAction<R> =
response: ClientResponse<R> response: ClientResponse<R>
} }
export type RequestReducer<R> = React.Reducer<RequestState<R>, RequestAction<R>>
export function requestReducer<R>(state: RequestState<R>, action: RequestAction<R>) { export function requestReducer<R>(state: RequestState<R>, action: RequestAction<R>) {
switch (action.type) { switch (action.type) {
case 'call': { case 'call': {

View file

@ -1,23 +1,18 @@
import React from 'react'; import React from 'react';
import invariant from 'tiny-invariant'; import invariant from 'tiny-invariant';
import { Endpoint, ExtractEndpointParams, ExtractEndpointResponse, ExtractEndpointVariables, Method } from './endpoint'; import { AnyEndpoint, Method } from './endpoint';
import { LazyRequestConfig, useLazyRequest } from './lazy-request-hook'; import { LazyRequestConfigFromEndpoint, useLazyRequest } from './lazy-request-hook';
export type RequestConfig<R, V, P> = Readonly< export type RequestConfigFromEndpoint<E extends AnyEndpoint> = Readonly<
LazyRequestConfig<R, V, P> LazyRequestConfigFromEndpoint<E>
& { & {
skip?: boolean, skip?: boolean,
} }
> >
export function useRequest< export function useRequest<E extends AnyEndpoint>(
E extends Endpoint<R, V, P>,
R = ExtractEndpointResponse<E>,
V = ExtractEndpointVariables<E>,
P = ExtractEndpointParams<E>
>(
endpoint: E, endpoint: E,
config?: RequestConfig<R, V, P>, config?: RequestConfigFromEndpoint<E>,
) { ) {
invariant( invariant(
endpoint.method !== Method.DELETE, endpoint.method !== Method.DELETE,

View file

@ -6,12 +6,14 @@ export declare enum Method {
PATCH = "PATCH", PATCH = "PATCH",
DELETE = "DELETE" DELETE = "DELETE"
} }
export declare type Endpoint<R, _V, P = never> = Readonly<{ export declare type Endpoint<R, V, P = unknown> = Readonly<{
_?: V;
method: Method; method: Method;
url: string | ((params: P) => string); url: string | ((params: P) => string);
headers?: Record<string, string>; headers?: Record<string, string>;
transformResponseData?: (data: any) => R; transformResponseData?: (data: any) => R;
}>; }>;
export declare type AnyEndpoint = Endpoint<any, any, any>;
export declare type ExtractEndpointResponse<E> = E extends Endpoint<infer R, any, any> ? R : E extends Endpoint<infer R, any> ? R : never; export declare type ExtractEndpointResponse<E> = E extends Endpoint<infer R, any, any> ? R : E extends Endpoint<infer R, any> ? R : never;
export declare type ExtractEndpointVariables<E> = E extends Endpoint<any, infer V, any> ? V : E extends Endpoint<any, infer V> ? V : never; export declare type ExtractEndpointVariables<E> = E extends Endpoint<any, infer V, any> ? V : E extends Endpoint<any, infer V> ? V : never;
export declare type ExtractEndpointParams<E> = E extends Endpoint<any, any, infer P> ? P : never; export declare type ExtractEndpointParams<E> = E extends Endpoint<any, any, infer P> ? P : never;

View file

@ -1,19 +1,20 @@
import { Endpoint, ExtractEndpointParams, ExtractEndpointResponse, ExtractEndpointVariables } from './endpoint'; import { AnyEndpoint, ExtractEndpointParams, ExtractEndpointResponse, ExtractEndpointVariables } from './endpoint';
import { PublicRequestState } from './reducer'; import { PublicRequestState } from './reducer';
import { ClientResponse } from './client'; import { ClientResponse } from './client';
export declare type LazyRequestConfig<R, V, P = never> = Readonly<{ export declare type LazyRequestConfig<R, V, P> = Readonly<{
variables?: V; variables?: V;
params?: P; params?: P;
headers?: Record<string, string>; headers?: Record<string, string>;
onComplete?: (data: R) => unknown; onComplete?: (data: R) => unknown;
onFailure?: (res: ClientResponse<R>) => unknown; onFailure?: (res: ClientResponse<R>) => unknown;
}>; }>;
export declare type LazyRequestHandlerConfig<R, V, P> = Readonly<LazyRequestConfig<R, V, P> & { export declare type LazyRequestConfigFromEndpoint<E extends AnyEndpoint> = LazyRequestConfig<ExtractEndpointResponse<E>, ExtractEndpointVariables<E>, ExtractEndpointParams<E>>;
export declare type LazyRequestHandlerConfig<E extends AnyEndpoint> = Readonly<LazyRequestConfigFromEndpoint<E> & {
force?: boolean; force?: boolean;
}>; }>;
export declare type RequestHandler<R, V, P> = (config?: LazyRequestHandlerConfig<R, V, P>) => Promise<R | null>; export declare type RequestHandler<E extends AnyEndpoint> = (config?: LazyRequestHandlerConfig<E>) => Promise<ExtractEndpointResponse<E> | null>;
export declare type RefetchRequestHandler = () => void; export declare type RefetchRequestHandler = () => void;
export declare type PublicRequestStateWithRefetch<R> = PublicRequestState<R> & { export declare type PublicRequestStateWithRefetch<E extends AnyEndpoint> = PublicRequestState<ExtractEndpointResponse<E>> & {
refetch: RefetchRequestHandler; refetch: RefetchRequestHandler;
}; };
export declare function useLazyRequest<E extends Endpoint<R, V, P>, R = ExtractEndpointResponse<E>, V = ExtractEndpointVariables<E>, P = ExtractEndpointParams<E>>(endpoint: E, config?: LazyRequestConfig<R, V, P>): [RequestHandler<R, V, P>, PublicRequestStateWithRefetch<R>]; export declare function useLazyRequest<E extends AnyEndpoint>(endpoint: E, config?: LazyRequestConfigFromEndpoint<E>): [RequestHandler<E>, PublicRequestStateWithRefetch<E>];

2
target/reducer.d.ts vendored
View file

@ -1,3 +1,4 @@
/// <reference types="react" />
import { ClientResponse } from './client'; import { ClientResponse } from './client';
export declare type PublicRequestState<R> = Readonly<{ export declare type PublicRequestState<R> = Readonly<{
data: R | null; data: R | null;
@ -21,6 +22,7 @@ export declare type RequestAction<R> = {
type: 'failure'; type: 'failure';
response: ClientResponse<R>; response: ClientResponse<R>;
}; };
export declare type RequestReducer<R> = React.Reducer<RequestState<R>, RequestAction<R>>;
export declare function requestReducer<R>(state: RequestState<R>, action: RequestAction<R>): { export declare function requestReducer<R>(state: RequestState<R>, action: RequestAction<R>): {
loading: boolean; loading: boolean;
isCalled: boolean; isCalled: boolean;

View file

@ -1,6 +1,6 @@
import { Endpoint, ExtractEndpointParams, ExtractEndpointResponse, ExtractEndpointVariables } from './endpoint'; import { AnyEndpoint } from './endpoint';
import { LazyRequestConfig } from './lazy-request-hook'; import { LazyRequestConfigFromEndpoint } from './lazy-request-hook';
export declare type RequestConfig<R, V, P> = Readonly<LazyRequestConfig<R, V, P> & { export declare type RequestConfigFromEndpoint<E extends AnyEndpoint> = Readonly<LazyRequestConfigFromEndpoint<E> & {
skip?: boolean; skip?: boolean;
}>; }>;
export declare function useRequest<E extends Endpoint<R, V, P>, R = ExtractEndpointResponse<E>, V = ExtractEndpointVariables<E>, P = ExtractEndpointParams<E>>(endpoint: E, config?: RequestConfig<R, V, P>): import("./lazy-request-hook").PublicRequestStateWithRefetch<R>; export declare function useRequest<E extends AnyEndpoint>(endpoint: E, config?: RequestConfigFromEndpoint<E>): import("./lazy-request-hook").PublicRequestStateWithRefetch<E>;