Skip to content

Commit

Permalink
feat: Add manual option to skip automatic execution
Browse files Browse the repository at this point in the history
Closes #6
  • Loading branch information
simoneb committed May 25, 2019
1 parent a1658e9 commit a98fba2
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 24 deletions.
70 changes: 61 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import useAxios from 'axios-hooks'

function App() {
const [{ data, loading, error }, refetch] = useAxios(
'https://jsonplaceholder.typicode.com/todos/1'
'https://api.myjson.com/bins/820fc'
)

if (loading) return <p>Loading...</p>
Expand All @@ -43,8 +43,6 @@ function App() {
}
```

> This example uses the awesome [JSONPlaceholder](https://jsonplaceholder.typicode.com/) API
## Documentation

### API
Expand All @@ -65,22 +63,24 @@ The package exports one default export and named exports:

`import useAxios, { configure, loadCache, serializeCache } from 'axios-hooks'`

### useAxios(url|config)
### useAxios(url|config, options)

The main React hook to execute HTTP requests. It accepts the same arguments as `axios`.
The main React hook to execute HTTP requests.

- `url|config` The request URL or [config](https://github.com/axios/axios#request-config) object
- `url|config` The request URL or [config](https://github.com/axios/axios#request-config) object, the same argument accepted by `axios`
- `options` a configuration object containing these keys:
- `manual` False by default. 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 hooks to execute the request manually, optionally providing additional arguments to `axios`.

Returns:

`[{ data, loading, error, response }, refetch]`
`[{ data, loading, error, response }, execute]`

- `data` The [success response](https://github.com/axios/axios#response-schema) data property (for convenient access)
- `loading` True if the request is in progress, otherwise False
- `error` The [error](https://github.com/axios/axios#handling-errors) value
- `response` The whole [success response](https://github.com/axios/axios#response-schema) object

- `refetch` Function to reload the data
- `execute([config])` Function to execute the request manually, bypassing the cache. It optionally accepts the same `config` object as `axios`, which is _shallow-merged_ with the config object provided when invoking the hook. Useful to provide arguments to non-GET requests.

### configure({ cache, axios })

Expand All @@ -103,6 +103,58 @@ Populates the cache with serialized data generated by `serializeCache`.

- `cache` The serializable representation of the request-response cache generated by `serializeCache`

## Manual requests

On the client, requests are executed when the component renders using a React `useEffect` hook.

This may be undesirable, as in the case of non-GET requests. By using the `manual` option you can skip the automatic execution of requests and use the return value of the hook to execute them manually, optionally providing configuration overrides to `axios`.

### Example

In the example below we use the `useAxios` hook twice. Once to load the data when the component renders, and once to submit data updates via a `PUT` request configured via the `manual` option.

[![Edit axios-hooks Manual Request](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/axioshooks-manual-request-bq9w4?fontsize=14)

```js
import useAxios from 'axios-hooks'

function App() {
const [{ data: getData, loading: getLoading, error: getError }] = useAxios(
'https://api.myjson.com/bins/820fc'
)

const [
{ data: putData, loading: putLoading, error: putError },
executePut
] = useAxios(
{
url: 'https://api.myjson.com/bins/820fc',
method: 'PUT'
},
{ manual: true }
)

function updateData() {
executePut({
data: {
...getData,
updatedAt: new Date().toISOString()
}
})
}

if (getLoading || putLoading) return <p>Loading...</p>
if (getError || putError) return <p>Error!</p>

return (
<div>
<button onClick={updateData}>update data</button>
<pre>{JSON.stringify(putData || getData, null, 2)}</pre>
</div>
)
}
```

## Configuration

Unless provided via the `configure` function, `axios-hooks` uses as defaults:
Expand All @@ -129,7 +181,7 @@ import LRU from 'lru-cache'
import Axios from 'axios'

const axios = Axios.create({
baseURL: 'https://jsonplaceholder.typicode.com/'
baseURL: 'https://api.myjson.com/'
})

const cache = new LRU({ max: 10 })
Expand Down
48 changes: 33 additions & 15 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
import React from 'react'
import Axios from 'axios'
import DefaultAxios from 'axios'
import LRU from 'lru-cache'

const actions = {
REQUEST_START: 'REQUEST_START',
REQUEST_END: 'REQUEST_END'
}

const initialState = {
loading: true
}

const ssrPromises = []

let cache = new LRU()
let axios = Axios
let axiosInstance = DefaultAxios

export function configure(options) {
if (options.axios) {
axios = options.axios
axiosInstance = options.axios
}

if (options.cache) {
Expand Down Expand Up @@ -48,7 +44,7 @@ async function cacheAdapter(config) {

delete config.adapter

const response = await axios(config)
const response = await axiosInstance(config)

const responseForCache = { ...response }
delete responseForCache.config
Expand All @@ -59,6 +55,12 @@ async function cacheAdapter(config) {
return response
}

function createInitialState(options) {
return {
loading: !options.manual
}
}

function reducer(state, action) {
switch (action.type) {
case actions.REQUEST_START:
Expand All @@ -81,34 +83,50 @@ function reducer(state, action) {
async function request(config, dispatch) {
try {
dispatch({ type: actions.REQUEST_START })
const response = await axios(config)
const response = await axiosInstance(config)
dispatch({ type: actions.REQUEST_END, payload: response })
} catch (err) {
dispatch({ type: actions.REQUEST_END, payload: err, error: true })
}
}

export default function useAxios(config) {
function executeRequestWithCache(config, dispatch) {
request({ ...config, adapter: cacheAdapter }, dispatch)
}

function executeRequestWithoutCache(config, dispatch) {
return request(config, dispatch)
}

export default function useAxios(config, options = { manual: false }) {
if (typeof config === 'string') {
config = {
url: config
}
}

const [state, dispatch] = React.useReducer(reducer, initialState)
const [state, dispatch] = React.useReducer(
reducer,
createInitialState(options)
)

if (typeof window === 'undefined') {
ssrPromises.push(axios({ ...config, adapter: cacheAdapter }))
ssrPromises.push(axiosInstance({ ...config, adapter: cacheAdapter }))
}

React.useEffect(() => {
request({ ...config, adapter: cacheAdapter }, dispatch)
if (!options.manual) {
executeRequestWithCache(config, dispatch)
}
}, [JSON.stringify(config)])

return [
state,
function refetch() {
return request(config, dispatch)
configOverride => {
return executeRequestWithoutCache(
{ ...config, ...configOverride },
dispatch
)
}
]
}

0 comments on commit a98fba2

Please sign in to comment.