-
-
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 }; |
Great snippets! Thanks 👍
@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
Thanks for the neat code. It will be helpful if you add example of how to use useRequest hook in functional component.