@pinkairship/use-data-fetch
Version:
A data fetch hook that stays out of your way.
768 lines (707 loc) • 18 kB
JavaScript
import React, { useCallback, useState } from 'react'
import MockAdapter from 'axios-mock-adapter'
import {
DataFetchProvider,
useDataFetch,
useFetchOnMount,
useFetchedArray,
useFetched,
} from '../src'
import { nanoid } from 'nanoid'
export function makeMockAxios(axiosInstance) {
const mock = new MockAdapter(axiosInstance)
// We want a slow return function
mock.onGet('/userinfo/slow').reply(async function () {
await setTimeout()
return [200, { id: nanoid() }]
})
mock.onGet('/userinfo').reply(200, { user: { id: 'my-id' } })
// We want a different id each time this endpoint is called, so make it
// a function
mock.onGet('/randomId').reply(function () {
const id = nanoid()
return [200, { id, data: id }]
})
mock.onGet(/echo\/[\w_\d-]+/).reply(function (config) {
const id = config.url.split('/')[2]
return [200, { id, data: id }]
})
mock.onPut(/echo\/[\w_\d-]+/).reply(function (config) {
const id = config.url.split('/')[2]
return [200, { id, data: `id: ${id} - I changed via a put!` }]
})
mock.onPatch(/echo\/[\w_\d-]+/).reply(function (config) {
const id = config.url.split('/')[2]
return [200, { id, data: `id: ${id} - I changed via a patch!` }]
})
mock.onPost('/echo').reply(function (config) {
const id = nanoid()
return [
200,
{
id,
data: `id: ${id} - I was created via a post! my data: ${
JSON.parse(config.data).message
}`,
},
]
})
mock.onDelete(/echo\/[\w_\d-]+/).reply(function () {
return [200, null]
})
mock.onGet('/randomIds').reply(function () {
const id = nanoid()
return [200, [{ id, data: `id: ${id}` }]]
})
mock.onGet('/randomIds/rootJson').reply(function () {
const id = nanoid()
return [
200,
{ root: [{ id, data: `id: ${id} - from a root json` }] },
]
})
mock.onPut(/randomIds\/[\w_\d-]+/).reply(function (config) {
const id = config.url.split('/')[2]
return [200, { id, data: `id: ${id} - I changed via a put!` }]
})
mock.onPatch(/randomIds\/[\w_\d-]+/).reply(function (config) {
const id = config.url.split('/')[2]
return [200, { id, data: `id: ${id} - I changed via a patch!` }]
})
mock.onPost('/randomIds').reply(function () {
const id = nanoid()
return [
200,
{ id, data: `id: ${id} - I was created via a post!` },
]
})
mock.onDelete(/randomIds\/[\w_\d-]+/).reply(function () {
return [200, []]
})
mock.onGet('/getWithData').reply(function (config) {
return [200, { message: config.data, config }]
})
mock.onPost('/message').reply(function (config) {
return [200, { message: config.data }]
})
mock.onPut('/replace').reply(function (config) {
return [200, { message: config.data }]
})
mock.onPatch('/update').reply(function (config) {
return [200, { message: config.data }]
})
mock.onDelete('/remove').reply(function (config) {
return [200, { message: config.data }]
})
}
export function makeDelayedMockAxios(axiosInstance) {
const mock = new MockAdapter(axiosInstance, { delayResponse: 500 })
// We want a slow return function
mock.onGet('/userinfo').reply(function () {
return [200, { id: nanoid() }]
})
}
export default function App() {
return (
<DataFetchProvider
screenReaderAlert={(message) => console.log(message)}
makeMockDataFetchInstance={makeMockAxios}
>
<div>
<MakePreloadGet />
<MakeGet />
<MakeGetWithParams />
<MakeQuery />
<MakeRandomGet />
<MakeGetWithData />
<MakeMethodDefinedCachedGet />
<MakeCallDefinedCachedGet />
<MakePost />
<MakePut />
<MakePatch />
<MakeDelete />
<MakeCustom />
<MakeGetWithSrAlert />
<MakeStoredGetFetch />
<UseManagedArrayFetch />
<UseManagedArrayFetchWithRootJson />
<UseManagedFetch />
<MakeCustomOverwriteData />
</div>
</DataFetchProvider>
)
}
export function MakeGet({
show = ({ data }) => alert(`User Id: ${data.user.id}`),
}) {
const { get } = useDataFetch('/userinfo')
return (
<div>
<input
type="button"
onClick={() => get().then(show)}
value="Make Get"
/>
</div>
)
}
export function MakePreloadGet({
show = ({ data }) => alert(`User Id: ${data.user.id}`),
}) {
const [users, setUsers] = useState([])
const updateStateHook = useCallback(
({ data: { user } }) => setUsers([...users, user]),
[users]
)
const { get } = useFetchOnMount('/userinfo', {
onSuccess: (request) => {
console.log('Preload request successful')
return request
},
hookOptions: {
updateStateHook,
},
})
return (
<div>
Check console to see preload success. Click the button below to
refetch. Check console also to see that the value was output
there for on success.
<div>
<input
type="button"
onClick={() => get().then(show)}
value="Make Preload Get Refetch"
/>
</div>
{users.map((user, i) => (
<div key={i}>Created id: {user.id}</div>
))}
</div>
)
}
export function MakeQuery({
show = ({ data }) => alert(`User Id: ${data.config.params.id}`),
}) {
const { query } = useDataFetch('/getWithData')
return (
<div>
<input
type="button"
onClick={() => query({ id: 'query-1234' }).then(show)}
value="Make Query"
/>
</div>
)
}
export function MakeGetWithParams({
show = ({ data }) => alert(`User Id: ${data.config.params.id}`),
}) {
const { get } = useDataFetch('/getWithData')
return (
<div>
<input
type="button"
onClick={() =>
get(undefined, {
requestConfig: { params: { id: '1234' } },
}).then(show)
}
value="Make Get with Params"
/>
</div>
)
}
export function MakeRandomGet({
show = ({ data: { id: id } }) => alert(`User Id: ${id}`),
buttonText = 'Make Random Get',
}) {
const { get } = useDataFetch('/randomId')
return (
<div>
<input
type="button"
onClick={() => get().then(show)}
value={buttonText}
/>
</div>
)
}
export function MakeMethodDefinedCachedGet({
show = ({ data: { id: id } }) => alert(`User Id: ${id}`),
}) {
const { get } = useDataFetch('/randomId', { useCache: true })
return (
<div>
<input
type="button"
onClick={() => get().then(show)}
value="Make Cached Get - Method Definition"
/>
</div>
)
}
export function MakeCallDefinedCachedGet({
show = ({ data: { id: id } }) => alert(`User Id: ${id}`),
}) {
const { get } = useDataFetch('/randomId', { useCache: false })
return (
<div>
<input
type="button"
onClick={() => get(undefined, { useCache: true }).then(show)}
value="Make Cached Get - Called"
/>
</div>
)
}
export function MakeGetWithData({
show = ({ data }) => alert(`Message: ${data.message}`),
}) {
const { get } = useDataFetch('/getWithData')
return (
<div>
<input
type="button"
onClick={() => get('my message of get').then(show)}
value="Make Get with Data"
/>
</div>
)
}
let getFunction
// Storing the data works with all other data fetch methods
export function MakeStoredGetFetch({ log = console.log }) {
const [ids, setIds] = useState([])
const [, triggerRerender] = useState()
const updateStateHook = useCallback(
({ data: id }) => setIds([...ids, id]),
[ids]
)
const { get } = useDataFetch('/randomId', {
updateStateHook,
})
if (getFunction == get) {
log('same get')
} else {
getFunction = get
log('different get')
}
return (
<div>
<input
type="button"
onClick={() => get()}
value="Make Stored Get"
/>
<input
type="button"
onClick={triggerRerender}
value="Stable Callback - Check Console for output"
/>
{ids.map((id) => (
<div key={id.id}>Created id: {id.id}</div>
))}
</div>
)
}
export function UseManagedArrayFetch() {
const [ids, setIds, requestState, dataFetch] = useFetchedArray(
'/randomIds',
{
hookOptions: { useCache: true },
}
)
return (
<div>
<input
type="button"
onClick={() => dataFetch.post()}
value="Make Managed Array State Post"
/>
<input
type="button"
onClick={() =>
dataFetch.put(ids[0]).then((err) => console.log(err))
}
value="Make Managed Array State Put"
/>
<input
type="button"
onClick={() => dataFetch.patch(ids[0])}
value="Make Managed Array State Patch"
/>
<input
type="button"
onClick={() => dataFetch.destroy(ids[0])}
value="Make Managed Array State Destroy"
/>
<input
type="button"
onClick={() => dataFetch.destroy(ids, ids)}
value="Make Managed Array Destroy All State"
/>
<input
type="button"
onClick={() =>
setIds([
...ids,
{
id: nanoid(),
data: 'I am created without a trip to the server!',
},
])
}
value="Make Managed Array State Update without Server Trip"
/>
<div>{requestState}</div>
{ids.map((id) => (
<div key={id.id}>{id.data}</div>
))}
</div>
)
}
export function UseManagedArrayFetchMultiple() {
const [ids, setIds, requestState, dataFetch] = useFetchedArray(
'/randomIds',
{
hookOptions: { useCache: true },
}
)
return (
<div>
<input
type="button"
onClick={() => {
for (let i = 0; i < 3; i++) {
dataFetch.post()
}
}}
value="Make Managed Array State Post Three"
/>
<input
type="button"
onClick={() => {
for (let i = 0; i < ids.length - 1; i++) {
dataFetch.destroy(ids[i])
}
}}
value="Make Managed Array State Destroy All But One"
/>
<div>{requestState}</div>
{ids.map((id) => (
<div key={id.id}>{id.data}</div>
))}
</div>
)
}
export function UseManagedArrayFetchWithRootJson() {
const [ids, , requestState] = useFetchedArray('/randomIds/rootJson')
return (
<div>
<div>With a Root Json object</div>
<div>{requestState}</div>
{ids.map((id) => (
<div key={id.id}>{id.data}</div>
))}
</div>
)
}
export function UseManagedFetch() {
const [id, setId, requestState, dataFetch] =
useFetched('/echo/myId')
return (
<div>
<input
type="button"
onClick={() => dataFetch.post({ message: 'Hi there!' })}
value="Make Managed State Post"
/>
<input
type="button"
onClick={() =>
dataFetch.put().then((err) => console.log(err))
}
value="Make Managed State Put"
/>
<input
type="button"
onClick={() => dataFetch.patch()}
value="Make Managed State Patch"
/>
<input
type="button"
onClick={() => dataFetch.destroy()}
value="Make Managed State Destroy"
/>
<input
type="button"
onClick={() =>
setId({
id: nanoid(),
data: 'I am created without a trip to the server!',
})
}
value="Make Managed State Update without Server Trip"
/>
<div>{requestState}</div>
<div>{id && id.data}</div>
</div>
)
}
// Alerting the screen reader works with all other data fetch methods
export function MakeGetWithSrAlert({
show = ({ data }) =>
alert(
`User Id: ${data.user.id} - - Check developer console for sr alert`
),
}) {
const { get } = useDataFetch('/userinfo', {
alertScreenReaderWith: 'Messages Came',
})
return (
<div>
<input
type="button"
onClick={() => get().then(show)}
value="Make Get And Alert Screen Reader"
/>
</div>
)
}
export function MakePost({
show = ({ data }) => alert(`Returned: ${data.message}`),
}) {
const { post } = useDataFetch('/message')
return (
<div>
<input
type="button"
onClick={() => post('my data').then(show)}
value="Make Post"
/>
</div>
)
}
export function MakePut({
show = ({ data }) => alert(`Replaced with: ${data.message}`),
}) {
const { put } = useDataFetch('/replace')
return (
<div>
<input
type="button"
onClick={() => put('different data').then(show)}
value="Make Put"
/>
</div>
)
}
export function MakePatch({
show = ({ data }) => alert(`Updated with: ${data.message}`),
}) {
const { patch } = useDataFetch('/update')
return (
<div>
<input
type="button"
onClick={() => patch('more different data').then(show)}
value="Make Patch"
/>
</div>
)
}
export function MakeDelete({
show = ({ data }) =>
alert(`Removed object with id: ${data.message}`),
}) {
const { destroy } = useDataFetch('/remove')
return (
<div>
<input
type="button"
onClick={() => destroy('id').then(show)}
value="Make Delete"
/>
</div>
)
}
export function MakeCustom({
show = ({ data }) => alert(`Custom data posted: ${data.message}`),
}) {
const { request } = useDataFetch(undefined, {
requestConfig: {
url: '/message',
method: 'post',
data: 'my-custom-message',
},
})
return (
<div>
<input
type="button"
onClick={() => request().then(show)}
value="Make Custom Call"
/>
</div>
)
}
export function MakeCustomOverwriteData({
show = ({ data }) => alert(`Custom data posted: ${data.message}`),
}) {
const { request } = useDataFetch(undefined, {
requestConfig: {
url: '/message',
method: 'post',
data: 'my-custom-message',
},
})
return (
<div>
<input
type="button"
onClick={() => request('overwritten data').then(show)}
value="Make Custom Call - Override Data"
/>
</div>
)
}
export function AppSecond() {
return (
<DataFetchProvider
screenReaderAlert={(message) => console.log(message)}
makeMockDataFetchInstance={makeMockAxios}
useCache={true}
>
<div>
<MakeRandomGet buttonText="Make Random Get - Context Override Cache" />
<MakeMethodDefinedCachedGetFalse />
<MakeCallDefinedCachedGetFalse />
</div>
</DataFetchProvider>
)
}
export function MakeMethodDefinedCachedGetFalse({
show = ({ data: { id: id } }) => alert(`User Id: ${id}`),
}) {
const { get } = useDataFetch('/randomId', { useCache: false })
return (
<div>
<input
type="button"
onClick={() => get().then(show)}
value="Make Cached Get - Method Definition - Override Cache Behavior"
/>
</div>
)
}
export function MakeCallDefinedCachedGetFalse({
show = ({ data: { id: id } }) => alert(`User Id: ${id}`),
}) {
const { get } = useDataFetch('/randomId', true)
return (
<div>
<input
type="button"
onClick={() => get(undefined, { useCache: false }).then(show)}
value="Make Cached Get - Called - Override Cache Behavior"
/>
</div>
)
}
export function AppThird() {
const [storedData, setStoredData] = useState([])
const updateHook = useCallback(
({ data }, other) => {
setStoredData([...storedData, data])
console.log(other)
},
[storedData]
)
return (
<DataFetchProvider
screenReaderAlert={(message) => console.log(message)}
makeMockDataFetchInstance={makeMockAxios}
updateStateHook={updateHook}
>
<div>
<MakeRandomGet
buttonText="Make Random Get - Provider State Updater"
show={() => {}}
/>
<MakeStoredGetFetchOverrideFromOnProvider />
<div>
{storedData.map((message) => (
<div key={message.id}>Created id: {message.id}</div>
))}
</div>
</div>
</DataFetchProvider>
)
}
// Storing the data works with all other data fetch methods
export function MakeStoredGetFetchOverrideFromOnProvider() {
const [ids, setIds] = useState([])
const updateStateHook = useCallback(
({ data: id }) => setIds([...ids, id]),
[ids]
)
const { get } = useDataFetch('/randomId', {
updateStateHook,
})
return (
<div>
<input
type="button"
onClick={() => get()}
value="Make Stored Get - Override Provider Store"
/>
{ids.map((id) => (
<div key={id.id}>Hook level - Created id: {id.id}</div>
))}
</div>
)
}
export function AppFourth() {
return (
<DataFetchProvider
screenReaderAlert={(message) => console.log(message)}
makeMockDataFetchInstance={makeDelayedMockAxios}
>
<MakeDelayedGet show={() => {}} />
<MakeErroredDelayedGet />
</DataFetchProvider>
)
}
export function MakeDelayedGet() {
const [loading, setLoading] = useState('pending')
const { get } = useDataFetch('/userinfo', {
requestStateListener: setLoading,
})
return (
<div>
<input
type="button"
onClick={() => get()}
value="Make Get - Success"
/>
<div>Request State: {loading}</div>
</div>
)
}
export function MakeErroredDelayedGet() {
const [loading, setLoading] = useState('pending')
const { get } = useDataFetch('/userinfo/notpresent', {
requestStateListener: setLoading,
})
return (
<div>
<input
type="button"
onClick={() => get()}
value="Make Get - Error"
/>
<div>Request State: {loading}</div>
</div>
)
}