2.0.5 • Published 11 months ago

use-react-api v2.0.5

Weekly downloads
-
License
ISC
Repository
github
Last release
11 months ago

use-react-api

This package makes http/ws request in react easier.

Features

  • React.Suspense support!
  • Lightweight
  • Auto and manual request
  • Fully customizable
  • Using axios
  • Multiple instance

Installation

  • with npm
npm i use-react-api axios
  • with yarn
yarn add use-react-api axios
  • with pnpm
pnpm add use-react-api axios

Setup

Configure your application with whatever configuration you want.

Basic Usage:

/* api.js */

import ReactApi from 'use-react-api'
const reactApi = ReactApi()

export default reactApi
export const { useApiOnce, useApi, useDataApi, createUseSuspense } =
  reactApi.hooks

/* Compontnt.js */

import { useApiOnce } from './api.js'

const Component = () => {
  const api = useApiOnce({ get: 'https://www.boredapi.com/api/activity' })

  if (api.error) return <div>{api.error}</div>
  if (api.loading) return <div>'Loading...'</div>
  return <div>{api.data.activity}</div>
}
import { useState } from 'react'
import { useApi } from './api.js'

const Component = () => {
  const [msg, setMsg] = useState()
  const api = useApi()

  const handleClick = async () => {
    const { data, ok } = await api.get('https://www.boredapi.com/api/activity')
    if (ok) {
      setMsg(data.activity)
    }
  }

  if (api.error) return <div>{api.error}</div>
  if (api.loading) return <div>'Loading...'</div>
  return (
    <button onClick={handleClick}>{msg ?? 'Click me to get message'}</button>
  )
}

Advanced usages of ReactApi:

ReactApi(AxiosInstanceConfig, ReactApiConfig)
// { instance, methods, useApi, useApiOnce, createSuspenseApi }

Check Axios instance config for AxiosInstanceConfig

ReactApiConfig options:

{
  _getSuccess?: (response: AxiosResponse) => any
  getSuccess?: (response: AxiosResponse) => any
  _getFail?: (err: AxiosError) => void
  getFail?: (err: AxiosError) => void
}

Option _getSuccess & _getFail:

These are middleware. When a request succeed this function is called, After finishing this function getSuccess | getFail starts.

Option getSuccess && getFail:

This gives AxiosResponse or AxiosError as first argument and send the returned value.

Example:

ReactApi(
  {},
  {
    _getSuccess: (response) => {
      console.log('Raw response', response)
    },
    getSuccess: (response) => {
      return response.status === 204
        ? true
        : response.data?.data ?? response.data
    },

    _getFail: (err) => {
      console.log('Raw error', err)
    },
    getFail: (err) => {
      return err.response?.data?.message ?? err.message
    },
  }
)

ReactApi() API:

This returns:

{
  methods: {…},
  hooks: {…},
  axiosInstance: ƒ
}

ReactApi().axiosInstance:

This is just the raw Axios instance

ReactApi().methods:

This is an object:

{
  requests: async Function,
  request: async Function,
  get: async Function,
  delete: async Function,
  head: async Function,
  options: async Function,
  post: async Function,
  put: async Function,
  patch: async Function,
}

All the functions takes axios params, check Axios instance config for more details. And they return {error, data, ok}

ReactApi().hooks.useApi(config):

Usages:

import { useState } from 'react'
import { useApi, useDataApi } from './api.js'

const Component = () => {
  const [msg, setMsg] = useState()

  // For normal usages
  const api = useApi()

  // When you don't want to use manual loading component, Just the React.Suspense
  const api = useApi({ suspense: true })

  // If you use this you don't need to set the data to a state. `api.data` will be the data from response
  const api = useDataApi({ suspense: true })

  const handleClick = async () => {
    const { data, ok } = await api.get('https://www.boredapi.com/api/activity')
    if (ok) {
      setMsg(data.activity)
    }
  }

  if (api.error) return <div>{api.error}</div>
  if (api.loading) return <div>'Loading...'</div>
  return (
    <button onClick={handleClick}>{msg ?? 'Click me to get message'}</button>
  )
}

The returned value looks like:

{
  loading: Boolean,
  data: Boolean,
  error: Boolean,

  requests: async Function,
  request: async Function,
  get: async Function,
  delete: async Function,
  head: async Function,
  options: async Function,
  post: async Function,
  put: async Function,
  patch: async Function,

  status: {
    loading: Boolean,
    data: Boolean,
    error: Boolean,
  },

  setStatus: {
    loading: Function, // Set custom loading status
    data: Function, // Set custom data status
    error: Function, // Set custom error status
    reset: Function // Reset status
  },

  methods: {
    request: async Function,
    get: async Function,
    delete: async Function,
    head: async Function,
    options: async Function,
    post: async Function,
    put: async Function,
    patch: async Function,
  }
}

These axios methods the returned value from getSuccess when there is no error else returns undefined

ReactApi().hooks.useApiOnce(...axios, onLoad):

import { useApiOnce } from './api.js'

const Component = () => {
  // Basic usages
  const api = useApiOnce({ get: 'https://www.boredapi.com/api/activity' })

  // With a function
  const api = useApiOnce(
    { get: 'https://www.boredapi.com/api/activity' },
    ([res]) => {
      console.log(res.data)
    }
  )

  if (api.error) return <div>{api.error}</div>
  if (api.loading) return <div>'Loading...'</div>
  return <div>{api.data.activity}</div>
}

The returned value looks like:

{
  loading: Boolean,
  data: T[],
  error: T[],

  retry: Function, // Retry the request

  status: {
    loading: Boolean,
    data: T[],
    error: T[]
  },

  setStatus: {
    loading: Function, // Set custom loading status
    data: Function, // Set custom data status
    error: Function, // Set custom error status
    reset: Function // Reset status
  }
}

ReactApi().hooks.createSuspense():

This function returns a hook that uses React.Suspense.

!! Warning !! -- Do not use this hook multiple times. Create a new hook for each component

import { createSuspense } from './api.js'
const useSuspenseApi = createSuspense()
const useSuspenseApi2 = createSuspense({ cache: true })

const Component = () => {
  // Basic usages
  const [bored, dummy] = useSuspenseApi(
    {url: 'https://www.boredapi.com/api/activity'}
    {url: 'https://dummyjson.com/products'}
  )

  // With a function
  const api2 = useSuspenseApi2(
    {url: 'https://www.boredapi.com/api/activity'}
    {url: 'https://dummyjson.com/products'}
    ([bored, dummy]) => {
      // This onLoad function will be called just once when the data loads for the first time
      console.log(bored.data, dummy.data)
    }
  )

  return <div>{bored.data.activity}</div>
}

What about AbortController?

I think it will be performence costly if a add it with everything. But there is an option.

import { useSignal } from 'use-react-api'
import { useApi } from './api.js'

const Component = () => {
  const api = useApi()
  const [signal, abort, isActive] = useAbortSignal()

  const handleSearch = async (e) => {
    const res = await api.get('/user/search', {
      signal: signal(),
    })
  }
}

Wait a min, what about WebSocket?

/* ws.js */

import { ReactWs } from 'use-react-api'
import { io } from 'socket.io-client'

const socket = io('http://localhost:8000')

socket.on('connect', () => {
  console.log('connected')
})

export const { useWs, useDataWs } = ReactWs(socket, {
  checkData(res) {
    return res.status === 'success'
  },
  formatData(res) {
    return res.data ?? res
  },
  formatError(res) {
    return res.message ?? res
  },
})

/* App.js */

import { useWs, useDataWs } from './ws'

const App = () => {
  const ws = useWs({ suspense: true })
  const ws = useDataWs({ suspense: true })

  const handleClick = async () => {
    // this is just like useApi().request but in socket way!
    const res = await ws.emit('hello')
    console.log(res)
  }

  const handleClick = async () => {
    // this is just like useApi().requests
    const [res1, res2] = await ws.emitAll('hello')
    console.log(res1, res2)
  }

  return <button onClick={handleClick}>Click</button>
}

Made by Nazmus Sayad with ❤️.

2.0.5

11 months ago

2.0.4

11 months ago

1.0.19

1 year ago

1.0.18

1 year ago

2.0.3

1 year ago

2.0.2

1 year ago

2.0.1

1 year ago

2.0.0

1 year ago

1.0.17

1 year ago

1.0.15

1 year ago

1.0.14

1 year ago

1.0.13

1 year ago

1.0.12

1 year ago

1.0.11

1 year ago

1.0.10

1 year ago

1.0.9

1 year ago

1.0.8

1 year ago

1.0.7

1 year ago

1.0.6

1 year ago

1.0.5

1 year ago

1.0.4

1 year ago

1.0.3

1 year ago

1.0.2

1 year ago

1.0.1

1 year ago

1.0.0

1 year ago