Skip to content

Instantly share code, notes, and snippets.

@cristovao-trevisan
Last active October 22, 2019 17:40
Show Gist options
  • Save cristovao-trevisan/01239119d4c1bd35cd3936d647026b87 to your computer and use it in GitHub Desktop.
Save cristovao-trevisan/01239119d4c1bd35cd3936d647026b87 to your computer and use it in GitHub Desktop.
Async Library API
// 1. Internal API
interface Resource<Data, Props = any> {
// here goes the INTERNAL API provided by the core that
// should be used by other libraries/frameworks to provide
// the real user interface
// We can also provide an API for debugging and other tools
subscribe: (cb: () => Data) => UnsubscribeFunction,
run: (props: Props, options: RunOptions) => Data,
abort: () => void,
update: (props: Props, currentData: Data) => Data,
// ...
}
// 2. Declaration API (used by users, not related to any framework)
declare function createResource<Data, Props> (options: {
// this fn should probably have some accessors, like: AbortableRest, Rest, PaginatedRest, InfiniteScrollRest, GraphQL, ...
fn: (props: Props, currentData: Data | null) => Promise<Data>,
// this hash function decides which props trigger another run (like useEffect's second param).
hash?: (props: Props) => string,
// here is the CLIENT-SIDE api for this resource, like:
// - caching
// - pooling
// - ssr
// - middleware options (caching and pooling should probably be some kind of middleware)
// - etc
}) : Resource<Data, Props>
// 3. Framework API
// React API (just a rough example)
// this should handle subscription and call run when needed (useEffect most likely)
declare function useResource<Data, Props> (resource: Resource<Data, Props>, props: Props) : State<Data>
type Metadata = {} | {
startedAt: Date,
duration: number,
runs: number,
// ...
}
interface InitialState {
data: null,
error: null,
pending: false,
resolved: false,
}
interface LoadingState {
data: null,
error: null,
pending: true,
resolved: false,
}
interface ResolvedState<Data> {
data: Data,
error: null,
pending: false,
resolved: true,
}
interface RejectedState<Data> {
data: null,
error: Error,
pending: false,
resolved: false,
}
interface ReloadingState<Data> {
data: Data,
error: null,
pending: true,
resolved: true,
}
type State<Data> = Metadata & (
InitialState
| LoadingState
| ResolvedState<Data>
| RejectedState<Data>
| ReloadingState<Data>
)
type UnsubscribeFunction = () => void
interface RunOptions {
reload: boolean = false,
}
// resource-definitions.ts (if using typescript)
interface UserInfo {
id: number,
name: string,
age: number,
photoUrl: string,
birthday: Date,
}
interface UserProps {
id: number,
}
type getUser = (id: string) => Promise<UserInfo>
// some resources.js file
import { createResource, useRest } from 'async-library'
export const userResource = createResource({
fn: useRest('/users/info/{id}'),
})
// ...
// in the component
import { useAsync } from 'react-async'
import { userResource } from '../../resources.js'
export const UserName = ({ id }) => {
const {
data: user,
pending,
error,
} = useAsync(UserResource, { id })
if (data) return <div> {user.name} </div>
if (error) return <div> {error} </div>
return <div> Loading... </div>
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment