import * as React from 'react'; import * as t from 'io-ts'; import { pipe } from 'fp-ts/lib/pipeable'; import * as E from 'fp-ts/lib/Either'; import * as TE from 'fp-ts/lib/TaskEither'; import * as RD from '@devexperts/remote-data-ts'; interface ApiOptions { token?: string; method?: string; path: string; data?: unknown; decoder?: t.Decoder; } // ritorna un taskeither, ed รจ quindi Lazy (ovvero a differenza delle promise // chiamare questa funzione non fa partire subito la chimata ad api) export function api(options: ApiOptions): TE.TaskEither { const decoder = options.decoder || t.any; const body = JSON.stringify(options.data); const headers = { Authorization: `Bearer ${options.token}`, 'Content-Type': 'application/json', Accept: 'application/json', }; const lazy = () => { const response = fetch(`/api${options.path}`, { method: options.method || 'get', headers, body, }); return response.then(x => x.json()); }; return pipe( TE.tryCatch(lazy, error => [ { value: error, context: [], message: 'network error' }, ]), TE.chain(x => TE.fromEither(decoder.decode(x))), ); } // react hook per gestire la struttura lazy di cui sopra, scatena la chimata // ad api e ci ritorna una struttura dati che rappresenta i vari stati di una // chiamata remota: not started (initial), pending, failure, success. export function useApi(te: TE.TaskEither): RD.RemoteData { const [data, setData] = React.useState>(RD.initial); React.useEffect(() => { setData(RD.pending); te().then(x => { setData( pipe( x, E.fold>(RD.failure, RD.success), ), ); }); }, []); return data; } // codice applicativo // Decoder a runtime const Property = t.type({ id: t.string, address: t.string, }); // estraiamo il tipo compile-time dal decoder runtime export type PropetyT = t.TypeOf; // componente react che che usa il decoder e le utility functions qui sopra // per mostrare i dati export const PropertyComponent = (props: { id: string }) => { const data = useApi( api({ path: `/property/${props.id}`, decoder: Property }), ); return pipe( data, RD.fold( () =>

api call not started

, () =>

loading data

, e =>

error loading api: ${JSON.stringify(e, null, 2)}

, property =>

{property.address}

, ), ); };