-
-
Save ulises-jeremias/a27b85e3eea083278188f24de955989b to your computer and use it in GitHub Desktop.
import { useCallback, useEffect, useState } from 'react'; | |
import { useSetRecoilState } from 'recoil'; | |
import { useKeycloak } from '@react-keycloak/web'; | |
import { commonNotification } from './common'; | |
/** | |
* Returns the auth info and some auth strategies. | |
* | |
*/ | |
export const useAuth = () => { | |
const [keycloak, initialized] = useKeycloak(); | |
const setNotification = useSetRecoilState(commonNotification); | |
const [user, setUser] = useState({}); | |
// fetch user profile | |
useEffect(() => { | |
if (!initialized) { | |
return; | |
} | |
const fetchUserInfo = async () => { | |
try { | |
const userProfile = await keycloak.loadUserProfile(); | |
setUser({ ...userProfile, fullName: `${userProfile.firstName} ${userProfile.lastName}` }); | |
} catch (err) { | |
setNotification({ isVisible: true, message: err.message }); | |
} | |
}; | |
if (keycloak.authenticated) { | |
fetchUserInfo(); | |
} | |
}, [keycloak, initialized]); | |
return { | |
isAuthenticated: !!keycloak.authenticated, | |
initialized, | |
meta: { | |
keycloak, | |
}, | |
token: keycloak.token, | |
user, | |
roles: keycloak.realmAccess, | |
login: useCallback(() => { keycloak.login(); }, [keycloak]), | |
logout: useCallback(() => { keycloak.logout(); }, [keycloak]), | |
register: useCallback(() => { keycloak.register(); }, [keycloak]), | |
}; | |
}; | |
export default { | |
useAuth, | |
}; |
import axios from 'axios'; | |
import { useState, useEffect } from 'react'; | |
import { useAuth } from 'auth-hook'; | |
/** | |
* Returns an authorizated axios instance | |
* | |
* @param {Object} config is the default config to be sent to the axios creator | |
* | |
* @return {Object} an object containing the axios instance and the initialized prop | |
* | |
*/ | |
export const useAxios = (config = {}) => { | |
const { token, initialized: authInitialized } = useAuth(); | |
const [initialized, setInitialized] = useState(false); | |
const [axiosInstance, setAxiosInstance] = useState({}); | |
useEffect(() => { | |
const instance = axios.create({ | |
...config, | |
headers: { | |
...(config.headers || {}), | |
'Content-Type': 'application/json', | |
'Access-Control-Allow-Origin': '*', | |
Authorization: authInitialized ? `Bearer ${token}` : undefined, | |
}, | |
}); | |
setAxiosInstance({ instance }); | |
setInitialized(true); | |
return () => { | |
setAxiosInstance({}); | |
setInitialized(false); | |
}; | |
}, [token, authInitialized]); | |
return { axios: axiosInstance.instance, initialized }; | |
}; | |
export default { useAxios }; |
import { atom } from 'recoil'; | |
// notification | |
export const commonNotification = atom({ | |
key: 'commonNotification', | |
default: { | |
isVisible: false, | |
message: '', | |
}, | |
}); |
import { useCallback } from 'react'; | |
// Hook defined thinking in the future. Actually it does not behave as a hook. | |
export const useErrorHandler = (hookOptions = {}) => { | |
/** | |
* Error handler | |
* | |
* En la función se define el flujo de la aplicación en caso de un error. | |
* | |
* @param {String | Object | Error} error | |
* @returns {String[2]} | |
* | |
*/ | |
const handleError = useCallback((error, options) => ( | |
// use error and options here | |
['message', ''] | |
), []); | |
return { | |
handleError, | |
}; | |
}; | |
export default { useErrorHandler }; |
import { useState, useCallback } from 'react'; | |
import { useHistory } from 'react-router-dom'; | |
import { useSetRecoilState } from 'recoil'; | |
import { commonNotification } from './common'; | |
import { useAxios } from './axios-hook'; | |
import { useErrorHandler } from './error-hook'; | |
const API_BASE_URL = '/api/'; | |
const REQUEST_TIMEOUT = 5; | |
/** | |
* | |
* @param {Object} options request options | |
* @param {String} options.url The request url | |
* @param {String} options.method The request http method | |
* @param {Object} requestParams.initialValue The response data initial value | |
* | |
* @see useAxios | |
* @see axiosDefaultConfig | |
* @see mapResponseToData | |
* | |
* @return {Object} return an object containing the data, isLoading and the request strategy. | |
* | |
*/ | |
export const useRequest = (options = {}, axiosConfig = {}) => { | |
const [data, setData] = useState(options.initialValue); | |
const [isLoading, setLoading] = useState(false); | |
const setNotification = useSetRecoilState(commonNotification); | |
const { handleError } = useErrorHandler(); | |
const { axios, initialized: axiosInitialized } = useAxios({ | |
...axiosDefaultConfig, | |
...axiosConfig, | |
}); | |
const history = useHistory(); | |
/** | |
* Specific request for options | |
* | |
* @param {Object} requestParams Request | |
* @param {Object} requestParams.params The request query params | |
* @param {Object} requestParams.data The request body data | |
* | |
*/ | |
const request = (requestParams) => { | |
if (!axiosInitialized) { | |
return; | |
} | |
const fetchData = async () => { | |
setLoading(true); | |
try { | |
const response = await axios({ ...options, ...requestParams }); | |
const responseData = mapResponseToData(response); | |
setData(responseData); | |
setLoading(false); | |
} catch (err) { | |
const [message, redirect] = handleError(err); | |
setLoading(false); | |
setNotification({ message, isVisible: true }); | |
if (redirect) { | |
history.push(redirect); | |
} | |
} | |
}; | |
fetchData(); | |
}; | |
const fetch = useCallback(request, [axiosInitialized]); | |
return { isLoading, data, fetch }; | |
}; | |
/** | |
* Request default config for axios creator | |
* | |
* @see API_BASE_URL | |
* @see REQUEST_TIMEOUT | |
*/ | |
const axiosDefaultConfig = { | |
baseURL: API_BASE_URL, | |
withCredentials: true, | |
timeout: REQUEST_TIMEOUT, | |
}; | |
/** | |
* Maps axios response to data | |
* | |
* @param {Object} response | |
* | |
* @return {Object} the response data | |
* @throws {Object} throws an object containing the axios response | |
*/ | |
export const mapResponseToData = (response) => { | |
const { data } = response; | |
return data; | |
}; | |
export default { useRequest }; |
@ulises-jeremias
I did a quick implementation but noticed that the first time, the request doesn't work as Axios initialisation would be pending. I need to make an additional call to make it work. Am I doing something wrong?
`
const { isLoading, data, fetch } = useRequest()
useEffect(() => {
let options = { url: '/applicant', method: 'GET', initialValue: [] }
fetch(options);
}, []);
`
@jayrp11 you can find a real usage example of the other hooks here! https://gist.github.com/ulises-jeremias/2360fefd8ded8d5a8f87c3258a2b5a0d
I'll add here some examples of the useRequest later 👌🏻
@praveencastelino I'll check it in a few and let you know if I can reproduce and what is the fix!
Thanks @ulises-jeremias
Ulises, tienes un ejemplo de como integrar todo este en una App? 🤔
@nappalm ahora mismo no tengo uno pero voy a ver de crear uno dentro de poco!
Thanks again for sharing
Great snippets! Thanks 👍