Skip to content

misantronic/wrap-request

Repository files navigation

wrap-request

a request wrapper for asynchronous operations

basic usage

const wrappedXhr = wrapRequest((config) => fetch('...'));

const { loading, fetched, error } = wrappedXhr;

const result = await wrappedXhr.request({ id: 1 });

pattern matching

based on the tc39-proposal for pattern matching you can display all states that your wrap-requests might enter.

const wrappedXhr = wrapRequest((config) => fetch('...'));

wrappedXhr.match({
    loading: () => 'Loading...',
    error: (e) => e.message,
    empty: () => 'No data.',
    fetched: (res) => res.data,
    default: () => 'Nothing to display'
});

react example

const MyComponent = () => {
    return wrappedXhr.match({
        loading: () => 'Loading...',
        error: (e) => e.message,
        empty: () => 'No data.',
        fetched: (res) => res.data,
        default: () => 'Nothing to display'
    });
};

default data

especially when dealing with lists it comes in handy to set a default value. from v7.0.0 on, when not setting defaultData, all of your data will be undefined by default when directly accessing it.

const wrappedXhr = wrapRequest(() => fetch('...'), { defaultData: [] });

pipe

sometimes it is useful, to directly pipe the result and keep a copy of the original data in the wrapper.

const wrappedXhr = wrapRequest(() => fetch('...'), {
    defaultData: []
}).pipe((res) => res.slice(0, 15));

const result = await wrappedXhr.request();

console.log(result); // capped list containing 15 items
console.log(wrappedXhr.$); // same as result
console.log(wrappedXhr.source); // list containing all items

you can also chain or use pipes as often as you like:

const wrappedXhr = wrapRequest(async () => [1, 2, 3, 4, 5], {
    defaultData: []
}).pipe((res) => res.map((num) => num.toString()));

await wrappedXhr.request();

const pipe1 = wrappedXhr.pipe((res) => res.slice(0, 2)); // [ '1', '2' ]
const pipe2 = pipe1.pipe((res) => res.slice(0, 1)); // [ '1' ]
const pipe3 = pipe2.pipe((res) => res[0]); // '1'

reset

Reset all wrapper-values to its initial state.

const wrappedXhr = wrapRequest(() => fetch('...'), {
    defaultData: []
});

await wrappedXhr.request();

wrappedXhr.reset();

metadata

You can save any metadata on the wrapper to store further informations.

const wrappedXhr = wrapRequest(() => fetch('...'), {
    metadata: (res) => ({
        fullName: `${res.firstname} ${res.lastname}`
    })
});

await wrappedXhr.request();

console.log(wrappedXhr.metadata);

error-handling

It is possible to notify the user at runtime that an error triggered in a wrap request has not been handled. This method is deactivated by default and must be opt-in. The notification will be output in the console. The default time-limit to show the notification is 8 seconds and can be overwritten via __wrapRequest__.UNHANDLED_ERROR_WARNING_TIMEOUT

import { __wrapRequest__ } from 'wrap-request';

__wrapRequest__.UNHANDLED_ERROR_WARNING = true;

const wrap = wrapRequest(() => {
    throw new Error('Something wrong');
});

wrap.request(); // will trigger the notification after 8 seconds as `error` was never accessed

streaming

The nature of promises is to resolve data only once. In some cases you need to update resolve multiple times f.e. when working with websockets. Enter streaming.

import websocket from 'my-websocket-lib';

const streamWr = wrapRequest.stream<{}, { id: string }>((update, resolve, params) => {
    websocket.on('update', updatedData => update(JSON.parse(updatedData)));
    websocket.on('close', () => resolve({}));
    websocket.connect(params.id);
});

streamWr.on('update', (data) => console.log('update', data));
streamWr.on('resolve', (data) => console.log('resolve', data));
streamWr.request({ id: 'ABCD1234HIJK' });

When working with mobx-wrap-request, all observable-values are updated when calling update / resolve that means when rendering data, you may not need events but receive streamlined updates in your component.

react hook

There is an implementation for working with react-hooks inside your components. react-wrap-request

mobx dependency

wrap-request used to have a direct dependency on mobx. this was removed in 3.0.0 please use mobx-wrap-request for further support.

pitfalls

typescript

please avoid setting your own generics when using wrapRequest. The problem here is, if you don't set all of the generics, chances are high that automatic type-inference will break.

❌ don't:

wrapRequest<MyArray[]>(() => [], { defaultData: [] });

✅ do:

wrapRequest(() => [] as MyArray[], { defaultData: [] });

If you really need to override all the generics, better make sure to set all of them:

wrapRequest<MyArray[], any, MyArray[], any, never[]>(() => []);

About

a request wrapper for asynchronous operations.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •