{"id":13450515,"url":"https://github.com/RyanRoll/react-use-api","last_synced_at":"2025-03-23T16:31:40.753Z","repository":{"id":34932406,"uuid":"186654376","full_name":"RyanRoll/react-use-api","owner":"RyanRoll","description":"Async HTTP request data for axios. Designed for diverse UI states, SSR and data pre-caching.","archived":false,"fork":false,"pushed_at":"2023-01-06T01:53:29.000Z","size":1690,"stargazers_count":62,"open_issues_count":26,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-04-24T18:55:29.055Z","etag":null,"topics":["axios","create-react-app","npm","npm-package","react","react-hooks","server-side-rendering","ssr","typescript","yarn","yarn-package"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RyanRoll.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-05-14T15:51:56.000Z","updated_at":"2023-10-17T08:34:28.000Z","dependencies_parsed_at":"2023-01-15T10:45:14.098Z","dependency_job_id":null,"html_url":"https://github.com/RyanRoll/react-use-api","commit_stats":null,"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RyanRoll%2Freact-use-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RyanRoll%2Freact-use-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RyanRoll%2Freact-use-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RyanRoll%2Freact-use-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RyanRoll","download_url":"https://codeload.github.com/RyanRoll/react-use-api/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":213324778,"owners_count":15570180,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["axios","create-react-app","npm","npm-package","react","react-hooks","server-side-rendering","ssr","typescript","yarn","yarn-package"],"created_at":"2024-07-31T07:00:35.511Z","updated_at":"2024-07-31T07:01:36.577Z","avatar_url":"https://github.com/RyanRoll.png","language":"TypeScript","funding_links":[],"categories":["Packages","TypeScript"],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003ch1\u003e\n    \u003cimg width=\"120\" src=\"./icon.svg\"\u003e\n    \u003cbr/\u003e\n    React useApi()\n    \u003cbr /\u003e\n    \u003cbr /\u003e\n  \u003c/h1\u003e\n\u003c/div\u003e\n\n[![npm](https://img.shields.io/npm/v/react-use-api?style=for-the-badge\u0026label=version\u0026color=e56565)](https://www.npmjs.com/package/react-use-api)\n[![Build Status](https://img.shields.io/travis/ryanroll/react-use-api/master?style=for-the-badge)](https://travis-ci.org/RyanRoll/react-use-api)\n[![Coverage Status](https://img.shields.io/coveralls/github/RyanRoll/react-use-api/master?style=for-the-badge)](https://coveralls.io/github/RyanRoll/react-use-api?branch=master)\n![npm type definitions](https://img.shields.io/npm/types/react-use-api?style=for-the-badge\u0026color=0277BD)\n![GitHub](https://img.shields.io/github/license/RyanRoll/react-use-api?style=for-the-badge\u0026color=5C6BC0)\n\n[Axios](https://github.com/axios/axios)-based React hooks for async HTTP request data. `react-use-api` feeds API data to React components when SSR (Server-Side Rendering), and caches the data to Front-End. `react-use-api` makes your code consistent between the two sides and also supports diverse UI states for your application.\n\n\u003e TypeScript Support\n\n\u003e Thread-safe SSR\n\n## Installation\n\n❗*Axios is a peer dependency (prerequisite) and it has to be installed*\n\n### NPM\n\n```bash\n// NPM\n$ npm i react-use-api axios\n// or yarn\n$ yarn add react-use-api axios\n```\n\n## Usage\n\n### Setup With ApiProvider\n\n```tsx\nimport React from 'react'\nimport ReactDom from 'react-dom'\nimport useApi, { ApiProvider } from 'react-use-api'\n\nimport App from './App'\n\n// This is optional from client side\nconst apiContext = {\n  settings: {\n    cache: new LRU\u003cstring, ReactUseApi.CacheData | any\u003e(),\n    axios: axios as AxiosStatic | AxiosInstance,\n    maxRequests: 50, // max requests count when running ReactDom.renderToString in SSR\n    useCacheData: true, // whether to use the cached api data come from server\n    alwaysUseCache: false, // whether to use the cached api data always for each api call\n    clearLastCacheWhenConfigChanges: true, // clear the last cache data with the last config when the config changes\n    debug: false,\n    clientCacheVar: '__USE_API_CACHE__', // the property name of window to save the cached api data come from server side\n    isSSR: (...args: any[]): boolean | void =\u003e typeof window === 'undefined',\n    renderSSR: (...args: any[]): string =\u003e '', // a callback to render SSR HTML string\n    shouldUseApiCache: (\n      config?: ReactUseApi.Config,\n      cacheKey?: string\n    ): boolean | void =\u003e true, // a callback to decide whether to use the cached api data\n  },\n}\nReactDom.render(\n  \u003cApiProvider context={apiContext}\u003e\n    \u003cApp /\u003e\n  \u003c/ApiProvider\u003e,\n  document.getElementById('root')\n)\n```\n\n### React Hooks\n\n```tsx\nimport React from 'react'\nimport useApi from 'react-use-api'\n\nexport const Main = () =\u003e {\n  const [data, { loading, error }, request] = useApi({\n    url: '/api/foo/bar',\n  })\n\n  return (\n    \u003c\u003e\n      {loading \u0026\u0026 \u003cdiv\u003eLoading...\u003c/div\u003e}\n      {error \u0026\u0026 \u003cdiv\u003e{error.response.data.errMsg}\u003c/div\u003e}\n      {data \u0026\u0026 (\n        \u003c\u003e\n          \u003cdiv\u003eHello! {data.username}\u003c/div\u003e\n          \u003cbutton onClick={request}\u003eReload\u003c/button\u003e\n        \u003c/\u003e\n      )}\n    \u003c/\u003e\n  )\n}\n```\n\n## Parameters\n\n### Types\n\n```ts\nconst [data, state, request] = useApi\u003cD = ReactUseApi.Data\u003e(\n  config: string | ReactUseApi.SingleConfig | ReactUseApi.MultiConfigs,\n  options?: ReactUseApi.Options | ReactUseApi.Options['handleData']\n)\n```\n\n### Code\n\n```tsx\nconst [data, state, request] = useApi(config, options)\n\n// request the API data again, omit options.useCache=true\nrequest(config?: ReactUseApi.Config, keepState = false)\n```\n\nWith a custom TypeScript data type\n\n```tsx\ninterface IMyData {\n  foo: string\n  bar: string\n}\nconst [data, state, request] = useApi\u003cIMyData\u003e(config, options)\n```\n\n## Advanced Usages\n\n### Fetching API data forcibly\n\n`useApi` calls API once only and retains data and state regardless of each time component rerendering unless the config changes. This act is same as invoking `request()`.\n\n```tsx\nimport React, { useState } from 'react'\nimport useApi from 'react-use-api'\n\nexport const Page = () =\u003e {\n  const [page, setPage] = useState(1)\n  const [data, { loading }, request] = useApi({\n    url: '/api/foo/bar',\n    params: {\n      page,\n    },\n  })\n  return (\n    \u003c\u003e\n      \u003cspan\u003e{data?.title ?? 'Hello'}\u003c/span\u003e\n      \u003cbutton onClick={() =\u003e setPage(++page)}\u003eNext\u003c/button\u003e\n    \u003c/\u003e\n  )\n}\n```\n\n### Cache mechanism\n\nIf your application works with SSR, `useApi` will use cached API data instead of calling API when rendering your application on client side for the first time.\n\nOn the other hand, `options.useCache` allows you to tag the API to be saved in cache and share it with the components using the same API.\n\n```tsx\nimport React from 'react'\nimport useApi from 'react-use-api'\n\nconst Card = (useCache) =\u003e {\n  const [data, , request] = useApi('/api/foo/bar', { useCache })\n  return (\n    \u003c\u003e\n      \u003c\u003e{data?.name}\u003c/\u003e\n      \u003cbutton onClick={request}\u003eRefresh\u003c/button\u003e // call api, never use cache data\n    \u003c/\u003e\n  )\n}\n\n// without cache and SSR\nconst Page = () =\u003e {\n  return (\n    \u003c\u003e\n      \u003cCard /\u003e // call api\n      \u003cCard /\u003e // call api\n    \u003c/\u003e\n  )\n}\n\n// with useCache\nconst Page = () =\u003e {\n  return (\n    \u003c\u003e\n      \u003cCard useCache={true} /\u003e // call api\n      \u003cCard useCache={true} /\u003e // use cache data\n      \u003cCard /\u003e // use cache data as well if cache exists\n      \u003cCard useCache={false} /\u003e // call api and never use cache data\n    \u003c/\u003e\n  )\n}\n```\n\n\u003e `options.useCache=false` means never using cache data\n\n### Decision to use cache data globally\n\n```tsx\nimport React from 'react'\nimport ReactDOM from 'react-dom'\nimport { ApiProvider, loadApiCache } from 'react-use-api'\n\nimport App from './components/App'\n\nloadApiCache()\n\nconst apiContext = {\n  settings: {\n    shouldUseApiCache(config, cacheKey) {\n      // return false to deny\n      if (config.url == '/api/v1/doNotUseCache') {\n        return false\n      }\n      return true // default to return true\n    },\n    alwaysUseCache: true, // set true to make ptions.useCache=true\n  },\n}\n\nReactDOM.render(\n  \u003cApiProvider context={apiContext}\u003e\n    \u003cApp /\u003e\n  \u003c/ApiProvider\u003e,\n  document.getElementById('root')\n)\n```\n\n### Manually cache data clearing\n\n```tsx\nimport React, { useContext } from 'react'\nimport { useApi, ApiContext } from 'react-use-api'\n\nconst App = (props) =\u003e {\n  const apiContext = useContext(ApiContext)\n  const { settings, clearCache } = apiContext\n  clearCache()\n}\n```\n\n### Skipping useApi\n\n```tsx\nimport React from 'react'\nimport useApi from 'react-use-api'\n\nconst Page = (props) =\u003e {\n  const [data] = useApi('/api/foo/bar', { skip: true }) // never call this API\n  return \u003c\u003e{!data \u0026\u0026 'No data'}\u003c/\u003e\n}\n```\n\n### Pagination or infinite scrolling\n\n```tsx\nimport React, { useState, useMemo, useCallback } from 'react'\nimport useApi from 'react-use-api'\n\nexport const Main = () =\u003e {\n  const [offset, setOffset] = useState(0)\n  const limit = 10\n  const options = useMemo(\n    () =\u003e ({\n      handleData,\n      dependencies: {\n        limit,\n      },\n    }),\n    [limit]\n  )\n  // hasMore is a custom state here\n  const [data, { loading, error, hasMore = true }, request] = useApi(\n    getAPiList(),\n    options\n  )\n  const loadMore = useCallback(() =\u003e {\n    const nextOffset = offset + limit\n    // fetch the data and keep the current state and prevData\n    request(getAPiList(nextOffset, limit), true)\n    setOffset(nextOffset)\n  }, [offset])\n\n  return (\n    \u003c\u003e\n      {loading \u0026\u0026 \u003cdiv\u003eLoading...\u003c/div\u003e}\n      {error \u0026\u0026 \u003cdiv\u003e{error.response.data.errMsg}\u003c/div\u003e}\n      {data \u0026\u0026 (\n        \u003c\u003e\n          {data.list.map(({ name }) =\u003e (\n            \u003cdiv key={name}\u003e{name}\u003c/div\u003e\n          ))}\n          {hasMore \u0026\u0026 \u003cbutton onClick={loadMore}\u003eLoad More\u003c/button\u003e}\n        \u003c/\u003e\n      )}\n    \u003c/\u003e\n  )\n}\n\nexport const getAPiList = (offset, limit) =\u003e ({\n  url: '/api/list',\n  params: {\n    offset,\n    limit,\n  },\n})\n\n// [IMPORTANT] Using any state setter in handleData is not allowed,\n// it will cause the component re-rendering infinitely while SSR rendering.\nexport const handleData = (state) =\u003e {\n  const { prevData = [], response, dependencies, error } = state\n  if (!error) {\n    const {\n      data: { userList },\n    } = response\n    const { limit } = dependencies\n    if (userList.length \u003c limit) {\n      // update hasMore\n      state.hasMore = false\n    }\n    return [...prevData, ...userList]\n  } else {\n    // show an error message from the api\n    console.log(error.response.data.msg)\n  }\n}\n```\n\n## Config\n\nThe config can be an [Axios Request Config](https://github.com/axios/axios#request-config) or a URL string.\n\n```tsx\nconst [data, state] = useApi('/api/foo/bar')\n// equals to\nconst [data, state] = useApi({\n  url: '/api/foo/bar',\n})\n```\n\n### Options [Optional]\n\n| Name          | Type                                          | default     | Description                                                                                                                                                                                                          |\n| ------------- | --------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| handleData    | Function(data: any, state: ReactUseApi.State) |             | A callback function to deal with the data of the API's response. **IMPORTANT** Using any state setter in handleData is dangerous, which will cause the component re-rendering infinitely while SSR rendering.        |\n| dependencies  | Object                                        |             | The additional needed data using in handleData. `NOTE`: \"dependencies\" is supposed to immutable due to React's rendering policy.                                                                                     |\n| shouldRequest | Function                                      | () =\u003e false | A callback to decide whether useApi re-fetches the API when `only re-rendering`. Returning true will trigger useApi to re-fetch. This option is helpful if you want to re-request an API when a route change occurs. |\n| watch         | any[]                                         | []          | An array of values that the effect depends on, this is the same as the second argument of useEffect.                                                                                                                 |\n| skip          | Boolean                                       | false       | Sets true to skip API call.                                                                                                                                                                                          |\n| useCache      | Boolean                                       | --          | Sets true to use cached API data if cache exists (calling API and then saves it if there is no cache). Sets false to call API always. By default, `useApi` uses the cached data provided from SSR.                   |\n\n## State\n\n### First State (before calling API)\n\nThe first state has only one property `loading` before calling API.\n\n| Name      | Type    | Default | Description                                       |\n| --------- | ------- | ------- | ------------------------------------------------- |\n| loading   | boolean | false   | To indicate whether calling api or not.           |\n| fromCache | boolean | false   | To tell whether the data come from SSR API cache. |\n\n#### Full State (after calling API)\n\nThe is the full state data structure after the api has responded.\n\n| Name          | Type              | Default   | Description                                                                                                                                                                       |\n| ------------- | ----------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| loading       | boolean           | false     | To indicate whether calling api or not.                                                                                                                                           |\n| fromCache     | boolean           | false     | To tell whether the data come from SSR API cache.                                                                                                                                 |\n| data          | any               | undefined | The processed data provided from `options.handleData`.                                                                                                                            |\n| response      | AxiosResponse     | undefined | The Axios' response.                                                                                                                                                              |\n| error         | AxiosError        | undefined | The Axios' error.                                                                                                                                                                 |\n| dependencies  | Object            | undefined | The additional needed data using in handleData. `NOTE`: \"dependencies\" is supposed to immutable due to React's rendering policy.                                                  |\n| prevData      | any               | undefined | The previous data of the previous state.                                                                                                                                          |\n| prevState     | ReactUseApi.State | undefined | The previous state.                                                                                                                                                               |\n| [custom data] | any               |           | You can add your own custom state data into the state by setting up in `options.handleData`. For example, `state.foo = 'bar'`. The data always be preserved whether re-rendering. |\n\n#### Request Function\n\nA function allows requesting API data again. This function will trigger re-render.\n\n| Name       | Type               | Default                         | Description                                                                                                                       |\n| ---------- | ------------------ | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |\n| config     | ReactUseApi.Config | The config passed from useApi() | An axios' config object to fetch API data.                                                                                        |\n| keepState  | boolean            | false                           | Set to true to maintain current state data, which facilitates combining previous data with current data, such as table list data. |\n| revalidate | boolean            | false                           | Set to true to fetch API without using cache data.                                                                                |\n\n### Server Side Rendering (SSR)\n\nThe biggest advantage of `react-use-api` is to make your code consistent for both client and server sides. Unlike using Next.js or Redux, these require you to fetch API data by yourself before feeding it to the React component. `react-use-api` does the chores for you and saves all your API data as cache for client side.\n\n`react-use-api` guarantees that the SSR for each HTTP request is thread-safe as long as passing the api context with SSR settings to `ApiProvider`.\n\nPlease be aware that no lifecycle methods will be invoked when SSR.\n\n#### SSR and injecting cached api data\n\n```tsx\n// server/render.js (based on Express framework)\nimport React from 'react'\nimport ReactDomServer from 'react-dom/server';\nimport { StaticRouter } from 'react-router-dom'\nimport { ApiProvider, injectSSRHtml } from 'react-use-api'\n\nimport App from '../../src/App'\n\nexport const render = async (req, axios) =\u003e {\n  const { url } = req\n  const apiContext = {\n    // configure your global options or SSR settings\n    settings: {\n      axios, // to set your custom axios instance\n      isSSR: () =\u003e true, // we are 100% sure here is SSR mode\n    },\n  }\n  const routerContext = {}\n  const renderSSR = () =\u003e\n    ReactDomServer.renderToString(\n      \u003cApiProvider context={apiContext}\u003e\n        \u003cStaticRouter location={url} context={routerContext}\u003e\n          \u003cApp /\u003e\n        \u003c/StaticRouter\u003e\n      \u003c/ApiProvider\u003e\n    )\n  const html = await injectSSRHtml(apiContext, renderSSR)\n  return html\n}\n```\n\n**The cache data has been inserted into your SSR HTML string as well.**\n\n\u003e The cache data will be cleaned up after calling loadApiCache() by default\n\n```html\n\u003cscript\u003e\n  window.__USE_API_CACHE__ = '[{ ... }]' // the cache data\n\u003c/script\u003e\n```\n\n#### SSR Settings of apiContext (ReactUseApi.CustomSettings)\n\n_Each property is optional_\n\n| Name                            | Type                                      | Default                                                   | Description                                                                                                                                       |\n| ------------------------------- | ----------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |\n| cache                           | LRU\u003cString, ReactUseApi.CacheData \\| any\u003e | new LRU()                                                 | The cache instance based on lru-cache                                                                                                             |\n| axios                           | AxiosStatic \\| AxiosInstance              | axios                                                     | axios instance (http client)                                                                                                                      |\n| maxRequests                     | number                                    | 100                                                       | The maximum of API requests when SSR                                                                                                              |\n| useCacheData                    | boolean                                   | true                                                      | Set true to inject JS cache data into html when calling `injectSSRHtml()`                                                                         |\n| alwaysUseCache                  | boolean                                   | false                                                     | Set true to use cache data always (equivalent to `options.useCache = true`)                                                                       |\n| clearLastCacheWhenConfigChanges | boolean                                   | true                                                      | This is default behavior that the cached data will be removed once the url config of useApi has been changed. Set false to remain the cached data |\n| debug                           | boolean                                   | true                                                      | Set true to get debug message from console                                                                                                        |\n| clientCacheVar                  | string                                    | 'USE_API_CACHE'                                           | The JS variable name of cache data                                                                                                                |\n| renderSSR                       | Function                                  | () =\u003e ''                                                  | A callback to render SSR string for injectSSRHtml()                                                                                               |\n| isSSR                           | Function                                  | () =\u003e typeof window === 'undefined'                       | A function to determine if the current environment is server                                                                                      |\n| shouldUseApiCache               | Function                                  | (config?: ReactUseApi.Config, cacheKey?: string): boolean | Returns true to enable useApi to get the API data from API cache, which is loaded by `loadApiCache`. Default is true                              |\n\n#### Arguments of injectSSRHtml\n\n```tsx\ninjectSSRHtml(\n  context: ReactUseApi.CustomContext,\n  renderSSR?: () =\u003e string,\n  postProcess?: (ssrHtml: string, apiCacheScript: string) =\u003e string, // a callback for after rendering SSR HTML string\n): string\n\n```\n\n#### SSR - Load cached API data\n\nPlease don't forget to invoke `loadApiCache()` to load the cached API data come from your server side. useApi will use the cache data instead of calling API when rendering your application for the first time.\n\n```tsx\n// src/index.jsx\nimport React from 'react'\nimport ReactDOM from 'react-dom'\nimport { ApiProvider, loadApiCache } from 'react-use-api'\n\nimport App from './components/App'\n\nconst rootElement = document.getElementById('root')\nconst method = rootElement.hasChildNodes() ? 'hydrate' : 'render'\n\nloadApiCache()\n\nReactDOM[method](\n  \u003cApiProvider\u003e\n    \u003cApp /\u003e\n  \u003c/ApiProvider\u003e,\n  rootElement\n)\n```\n\n## TypeScript Support\n\nAll the associated types are provided by the namespace [ReactUseApi](src/typings.d.ts) as long as importing `react-use-api`.\n\n\u003e NOTE, this only works if you set up compilerOptions.typeRoots: [\"node_modules/@types\"] in your tsconfig.json.\n\n\u003e Support TypeScript v2.9+ only\n\n## License\n\nMIT\n\nIcons made by [Eucalyp](https://www.flaticon.com/authors/eucalyp) from [www.flaticon.com](https://www.flaticon.com) is licensed by [CC 3.0 BY](http://creativecommons.org/licenses/by/3.0).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRyanRoll%2Freact-use-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FRyanRoll%2Freact-use-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRyanRoll%2Freact-use-api/lists"}