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