1
0
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:
Sergio Betanzos 2021-06-23 09:41:20 +02:00
parent 08c0a987ba
commit 8bcc610503
No known key found for this signature in database
GPG Key ID: E3E704F097737136
2 changed files with 151 additions and 93 deletions

View File

@ -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

View File

@ -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