diff --git a/README.md b/README.md index 09628aee..997e7ff3 100644 --- a/README.md +++ b/README.md @@ -49,10 +49,10 @@ function App() { ### API - [useAxios](#useaxiosurlconfig-options) -- [configure](#configure-cache-axios-) +- [configure](#configure-cache-axios-defaultoptions) - [serializeCache](#serializeCache) - [loadCache](#loadcachecache) -- [makeUseAxios](#makeuseaxios-cache-axios-) +- [makeUseAxios](#makeuseaxios-cache-axios-defaultoptions) ### Examples @@ -92,6 +92,7 @@ The main React hook to execute HTTP requests. - `options` - An options object. - `manual` ( `false` ) - If true, the request is not executed immediately. Useful for non-GET requests that should not be executed when the component renders. Use the `execute` function returned when invoking the hook to execute the request manually. - `useCache` ( `true` ) - Allows caching to be enabled/disabled for the hook. It doesn't affect the `execute` function returned by the hook. + - `ssr` ( `true` ) - Enables or disables SSR support **Returns** @@ -112,12 +113,13 @@ The main React hook to execute HTTP requests. A promise containing the response. If the request is unsuccessful, an exception is thrown and must be handled manually. -### configure({ cache, axios }) +### configure({ cache, axios, defaultOptions }) -Allows to provide custom instances of cache and axios. +Allows to provide custom instances of cache and axios and to override the default options. - `cache` An instance of [lru-cache](https://github.com/isaacs/node-lru-cache), or `false` to disable the cache - `axios` An instance of [axios](https://github.com/axios/axios#creating-an-instance) +- `defaultOptions` An object overriding the default Hook options. It will be merged with the default options. ### serializeCache() @@ -133,12 +135,13 @@ Populates the cache with serialized data generated by `serializeCache`. - `cache` The serializable representation of the request-response cache generated by `serializeCache` -### makeUseAxios({ cache, axios }) +### makeUseAxios({ cache, axios, defaultOptions }) -Creates an instance of the `useAxios` hook configured with the supplied cache and axios instance. +Creates an instance of the `useAxios` hook configured with the supplied cache, axios instance and default options. - `cache` An instance of [lru-cache](https://github.com/isaacs/node-lru-cache), or `false` to disable the cache - `axios` An instance of [axios](https://github.com/axios/axios#creating-an-instance) +- `defaultOptions` An object overriding the default Hook options. It will be merged with the default options. **Returns** @@ -159,6 +162,7 @@ Unless provided via the `configure` function, `axios-hooks` uses as defaults: - `axios` - the default `axios` package export - `cache` - a new instance of the default `lru-cache` package export, with no arguments +- `defaultOptions` - `{ manual: false, useCache: true, ssr: true }` These defaults may not suit your needs, for example: diff --git a/src/index.d.ts b/src/index.d.ts index 84afd9bf..dddf2dd4 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -18,6 +18,7 @@ export interface ResponseValues { export interface Options { manual?: boolean useCache?: boolean + ssr?: boolean } export interface RefetchOptions { @@ -27,6 +28,7 @@ export interface RefetchOptions { export interface ConfigureOptions { axios?: AxiosInstance | AxiosStatic | any cache?: LRUCache | false + defaultOptions?: Options } export interface UseAxios { @@ -66,4 +68,4 @@ export function resetConfigure(): void // private export const __ssrPromises: Promise[] -export function makeUseAxios(options: ConfigureOptions): UseAxios +export function makeUseAxios(options?: ConfigureOptions): UseAxios diff --git a/src/index.js b/src/index.js index c5ecaf93..e2303ee5 100644 --- a/src/index.js +++ b/src/index.js @@ -7,6 +7,12 @@ const actions = { REQUEST_END: 'REQUEST_END' } +const DEFAULT_OPTIONS = { + manual: false, + useCache: true, + ssr: true +} + const useAxios = makeUseAxios() const { @@ -50,15 +56,17 @@ function configToObject(config) { return config } -export function makeUseAxios(configurationOptions) { +export function makeUseAxios(configureOptions) { let cache let axiosInstance + let defaultOptions const __ssrPromises = [] function resetConfigure() { cache = new LRU() axiosInstance = StaticAxios + defaultOptions = DEFAULT_OPTIONS } function configure(options = {}) { @@ -69,10 +77,14 @@ export function makeUseAxios(configurationOptions) { if (options.cache !== undefined) { cache = options.cache } + + if (options.defaultOptions !== undefined) { + defaultOptions = { ...DEFAULT_OPTIONS, ...options.defaultOptions } + } } resetConfigure() - configure(configurationOptions) + configure(configureOptions) function loadCache(data) { cache.load(data) @@ -193,7 +205,7 @@ export function makeUseAxios(configurationOptions) { ) options = React.useMemo( - () => ({ manual: false, useCache: true, ...options }), + () => ({ ...defaultOptions, ...options }), // eslint-disable-next-line react-hooks/exhaustive-deps [JSON.stringify(options)] ) @@ -205,7 +217,7 @@ export function makeUseAxios(configurationOptions) { createInitialState(config, options) ) - if (typeof window === 'undefined' && !options.manual) { + if (typeof window === 'undefined' && options.ssr && !options.manual) { useAxios.__ssrPromises.push(axiosInstance(config)) } diff --git a/test/index.test.jsx b/test/index.test.jsx index 4b05e867..6c17927a 100644 --- a/test/index.test.jsx +++ b/test/index.test.jsx @@ -58,7 +58,7 @@ describe('makeUseAxios', () => { }) it('should not throw', () => { - expect(makeUseAxios({})).toBeTruthy() + expect(makeUseAxios()).toBeTruthy() }) it('should provide a custom implementation of axios', () => { @@ -107,8 +107,48 @@ describe('makeUseAxios', () => { }) }) + describe('default hook options', () => { + describe('manual', () => { + it('should override default manual option', () => { + const setup = makeSetup( + makeUseAxios({ defaultOptions: { manual: true } }) + ) + + setup() + + expect(axios).not.toHaveBeenCalled() + }) + }) + + describe('useCache', () => { + it('should override default useCache option', async () => { + const setup = makeSetup( + makeUseAxios({ defaultOptions: { useCache: false } }) + ) + + axios.mockResolvedValue({ data: 'whatever' }) + + const { waitForNextUpdate, unmount } = setup() + + await waitForNextUpdate() + + unmount() + + await setup().waitForNextUpdate() + + expect(axios).toHaveBeenCalledTimes(2) + }) + }) + + describe('ssr', () => { + it('should be able to set ssr option', () => { + makeSetup(makeUseAxios({ defaultOptions: { ssr: false } })) + }) + }) + }) + describe('standard tests', () => { - const useAxios = makeUseAxios({}) + const useAxios = makeUseAxios() standardTests( useAxios, @@ -942,6 +982,42 @@ function standardTests( expect(axios).toHaveBeenCalledTimes(2) }) }) + + describe('default hook options', () => { + describe('manual', () => { + it('should override default manual option', () => { + configure({ defaultOptions: { manual: true } }) + + setup() + + expect(axios).not.toHaveBeenCalled() + }) + }) + + describe('useCache', () => { + it('should override default useCache option', async () => { + configure({ defaultOptions: { useCache: false } }) + + axios.mockResolvedValue({ data: 'whatever' }) + + const { waitForNextUpdate, unmount } = setup() + + await waitForNextUpdate() + + unmount() + + await setup().waitForNextUpdate() + + expect(axios).toHaveBeenCalledTimes(2) + }) + }) + + describe('ssr', () => { + it('should be able to set ssr option', () => { + configure({ defaultOptions: { ssr: false } }) + }) + }) + }) }) describe('loadCache', () => { diff --git a/test/index.test.ssr.jsx b/test/index.test.ssr.jsx index 40e4eb5d..396ff07a 100644 --- a/test/index.test.ssr.jsx +++ b/test/index.test.ssr.jsx @@ -2,13 +2,16 @@ import axios from 'axios' import React from 'react' import ReactDOM from 'react-dom/server' -import useAxios, { __ssrPromises } from '../src' +import { makeUseAxios } from '../src' import { mockCancelToken } from './testUtils' jest.mock('axios') +let useAxios + beforeEach(() => { mockCancelToken(axios) + useAxios = makeUseAxios() }) function DummyComponent(props) { @@ -17,16 +20,22 @@ function DummyComponent(props) { return null } -it('should not populate promises on server when manual', () => { +it('should not populate promises on server when manual=true', () => { ReactDOM.renderToString() - expect(__ssrPromises.length).toBe(0) + expect(useAxios.__ssrPromises.length).toBe(0) +}) + +it('should not populate promises on server when ssr=false', () => { + ReactDOM.renderToString() + + expect(useAxios.__ssrPromises.length).toBe(0) }) -it('should populate promises on server', () => { +it('should populate promises on server with default options', () => { axios.mockResolvedValueOnce({ data: 'whatever' }) ReactDOM.renderToString() - expect(__ssrPromises.length).toBe(1) + expect(useAxios.__ssrPromises.length).toBe(1) })