mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-16 22:50:10 +03:00
F OpenNebula/one#5422: Add reducer to fetch hooks
This commit is contained in:
parent
08c0a987ba
commit
8bcc610503
@ -1,64 +1,90 @@
|
||||
import { useState, useCallback, useEffect, useRef } from 'react'
|
||||
import { debounce } from '@material-ui/core'
|
||||
import { useReducer, useCallback, useEffect, useRef } from 'react'
|
||||
import { fakeDelay } from 'client/utils'
|
||||
import { console } from 'window-or-global'
|
||||
|
||||
const useRequest = request => {
|
||||
const [data, setData] = useState(undefined)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [reloading, setReloading] = useState(false)
|
||||
const [error, setError] = useState(false)
|
||||
const isMounted = useRef(true)
|
||||
const STATUS = {
|
||||
INIT: 'INIT',
|
||||
PENDING: 'PENDING',
|
||||
ERROR: 'ERROR',
|
||||
FETCHED: 'FETCHED'
|
||||
}
|
||||
|
||||
useEffect(() => () => { isMounted.current = false }, [])
|
||||
const ACTIONS = {
|
||||
REQUEST: 'REQUEST',
|
||||
SUCCESS: 'SUCCESS',
|
||||
FAILURE: 'FAILURE'
|
||||
}
|
||||
|
||||
const doFetch = useCallback(
|
||||
debounce(payload =>
|
||||
request(payload)
|
||||
.then(response => {
|
||||
if (isMounted.current) {
|
||||
if (response !== undefined) {
|
||||
setData(response)
|
||||
setError(false)
|
||||
} else setError(true)
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
console.log('error', e)
|
||||
if (isMounted.current) {
|
||||
setData(undefined)
|
||||
setError(true)
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (isMounted.current) {
|
||||
setLoading(false)
|
||||
setReloading(false)
|
||||
}
|
||||
})
|
||||
), [isMounted])
|
||||
const INITIAL_STATE = {
|
||||
status: STATUS.INIT,
|
||||
error: undefined,
|
||||
data: undefined,
|
||||
loading: false,
|
||||
reloading: false
|
||||
}
|
||||
|
||||
const fetchReducer = (state, action) => {
|
||||
const { type, payload, reload = false } = action
|
||||
|
||||
return {
|
||||
[ACTIONS.REQUEST]: {
|
||||
...INITIAL_STATE,
|
||||
status: STATUS.PENDING,
|
||||
[reload ? 'reloading' : 'loading']: true
|
||||
},
|
||||
[ACTIONS.SUCCESS]: {
|
||||
...INITIAL_STATE,
|
||||
status: STATUS.FETCHED,
|
||||
data: payload
|
||||
},
|
||||
[ACTIONS.FAILURE]: {
|
||||
...INITIAL_STATE,
|
||||
status: STATUS.ERROR,
|
||||
error: payload
|
||||
}
|
||||
}[type] ?? state
|
||||
}
|
||||
|
||||
const useFetch = request => {
|
||||
const cancelRequest = useRef(false)
|
||||
const [state, dispatch] = useReducer(fetchReducer, INITIAL_STATE)
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
cancelRequest.current = true
|
||||
}
|
||||
}, [])
|
||||
|
||||
const doFetch = useCallback(async (payload, reload = false) => {
|
||||
dispatch({ type: ACTIONS.REQUEST, reload })
|
||||
|
||||
try {
|
||||
const response = await request(payload)
|
||||
|
||||
if (response === undefined) throw response
|
||||
if (cancelRequest.current) return
|
||||
|
||||
dispatch({ type: ACTIONS.SUCCESS, payload: response })
|
||||
} catch (error) {
|
||||
if (cancelRequest.current) return
|
||||
|
||||
const errorMessage = typeof error === 'string' ? error : error?.message
|
||||
|
||||
dispatch({ type: ACTIONS.FAILURE, payload: errorMessage })
|
||||
}
|
||||
}, [request])
|
||||
|
||||
const fetchRequest = useCallback((payload, options = {}) => {
|
||||
const { reload = false, delay = 0 } = options
|
||||
|
||||
if (!(Number.isInteger(delay) && delay >= 0)) {
|
||||
console.error(`
|
||||
Delay must be a number >= 0!
|
||||
If you're using it as a function, it must also return a number >= 0.`)
|
||||
}
|
||||
|
||||
if (isMounted.current) {
|
||||
reload ? setReloading(true) : setLoading(true)
|
||||
}
|
||||
fakeDelay(delay).then(() => doFetch(payload, reload))
|
||||
}, [request])
|
||||
|
||||
fakeDelay(delay).then(() => doFetch(payload))
|
||||
}, [isMounted])
|
||||
|
||||
return {
|
||||
data,
|
||||
fetchRequest,
|
||||
loading,
|
||||
reloading,
|
||||
error
|
||||
}
|
||||
return { ...state, fetchRequest }
|
||||
}
|
||||
export default useRequest
|
||||
export default useFetch
|
||||
|
@ -1,58 +1,90 @@
|
||||
import { useState, useCallback, useEffect, useRef } from 'react'
|
||||
import { debounce } from '@material-ui/core'
|
||||
import { useReducer, useCallback, useEffect, useRef } from 'react'
|
||||
import { fakeDelay } from 'client/utils'
|
||||
|
||||
const useRequestAll = () => {
|
||||
const [data, setData] = useState(undefined)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [reloading, setReloading] = useState(false)
|
||||
const [error, setError] = useState(false)
|
||||
const isMounted = useRef(true)
|
||||
const STATUS = {
|
||||
INIT: 'INIT',
|
||||
PENDING: 'PENDING',
|
||||
ERROR: 'ERROR',
|
||||
FETCHED: 'FETCHED'
|
||||
}
|
||||
|
||||
useEffect(() => () => { isMounted.current = false }, [])
|
||||
const ACTIONS = {
|
||||
REQUEST: 'REQUEST',
|
||||
SUCCESS: 'SUCCESS',
|
||||
FAILURE: 'FAILURE'
|
||||
}
|
||||
|
||||
const doFetchs = useCallback(
|
||||
debounce((requests, onError) =>
|
||||
Promise
|
||||
.all(requests)
|
||||
.then(response => {
|
||||
if (isMounted.current) {
|
||||
if (response !== undefined) {
|
||||
setData(response)
|
||||
setError(false)
|
||||
} else setError(true)
|
||||
const INITIAL_STATE = {
|
||||
status: STATUS.INIT,
|
||||
error: undefined,
|
||||
data: undefined,
|
||||
loading: false,
|
||||
reloading: false
|
||||
}
|
||||
|
||||
isMounted.current && setLoading(false)
|
||||
isMounted.current && setReloading(false)
|
||||
}
|
||||
// }).catch(onError)
|
||||
}).catch(err => {
|
||||
setError(true)
|
||||
onError?.(err)
|
||||
})
|
||||
), [isMounted])
|
||||
const fetchReducer = (state, action) => {
|
||||
const { type, payload, reload = false } = action
|
||||
|
||||
return {
|
||||
[ACTIONS.REQUEST]: {
|
||||
...INITIAL_STATE,
|
||||
status: STATUS.PENDING,
|
||||
[reload ? 'reloading' : 'loading']: true
|
||||
},
|
||||
[ACTIONS.SUCCESS]: {
|
||||
...INITIAL_STATE,
|
||||
status: STATUS.FETCHED,
|
||||
data: payload
|
||||
},
|
||||
[ACTIONS.FAILURE]: {
|
||||
...INITIAL_STATE,
|
||||
status: STATUS.ERROR,
|
||||
error: payload
|
||||
}
|
||||
}[type] ?? state
|
||||
}
|
||||
|
||||
const useFetchAll = () => {
|
||||
const cancelRequest = useRef(false)
|
||||
const [state, dispatch] = useReducer(fetchReducer, INITIAL_STATE)
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
cancelRequest.current = true
|
||||
}
|
||||
}, [])
|
||||
|
||||
const doFetches = useCallback(async (requests, reload = false) => {
|
||||
dispatch({ type: ACTIONS.REQUEST, reload })
|
||||
|
||||
try {
|
||||
const response = Promise.all(requests)
|
||||
|
||||
if (response === undefined) throw response
|
||||
if (cancelRequest.current) return
|
||||
|
||||
dispatch({ type: ACTIONS.SUCCESS, payload: response })
|
||||
} catch (error) {
|
||||
if (cancelRequest.current) return
|
||||
|
||||
const errorMessage = typeof error === 'string' ? error : error?.message
|
||||
|
||||
dispatch({ type: ACTIONS.FAILURE, payload: errorMessage })
|
||||
}
|
||||
}, [])
|
||||
|
||||
const fetchRequest = useCallback((requests, options = {}) => {
|
||||
const { reload = false, delay = 0 } = options
|
||||
|
||||
const fetchRequestAll = useCallback((requests, options = {}) => {
|
||||
const { reload = false, delay = 0, onError } = options
|
||||
if (!(Number.isInteger(delay) && delay >= 0)) {
|
||||
console.error(`
|
||||
Delay must be a number >= 0!
|
||||
If you're using it as a function, it must also return a number >= 0.`)
|
||||
}
|
||||
|
||||
if (isMounted.current) {
|
||||
reload ? setReloading(true) : setLoading(true)
|
||||
}
|
||||
fakeDelay(delay).then(() => doFetches(requests, reload))
|
||||
}, [])
|
||||
|
||||
fakeDelay(delay).then(() => doFetchs(requests, onError))
|
||||
}, [isMounted])
|
||||
|
||||
return {
|
||||
data,
|
||||
fetchRequestAll,
|
||||
loading,
|
||||
reloading,
|
||||
error
|
||||
}
|
||||
return { ...state, fetchRequest }
|
||||
}
|
||||
export default useRequestAll
|
||||
export default useFetchAll
|
||||
|
Loading…
x
Reference in New Issue
Block a user