Skip to content

Instantly share code, notes, and snippets.

@ulises-jeremias
Last active January 3, 2024 06:39
Show Gist options
  • Star 18 You must be signed in to star a gist
  • Fork 10 You must be signed in to fork a gist
  • Save ulises-jeremias/a27b85e3eea083278188f24de955989b to your computer and use it in GitHub Desktop.
Save ulises-jeremias/a27b85e3eea083278188f24de955989b to your computer and use it in GitHub Desktop.
Examples of hooks utils using axios, recoil.js, keycloak-js, @react-keycloak/web and more.
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 };
@jayrp11
Copy link

jayrp11 commented Apr 26, 2021

Thanks for the neat code. It will be helpful if you add example of how to use useRequest hook in functional component.

@emrivero
Copy link

emrivero commented Dec 9, 2021

Great snippets! Thanks 👍

@praveencastelino
Copy link

praveencastelino commented Mar 28, 2022

@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);
}, []);

`

@ulises-jeremias
Copy link
Author

ulises-jeremias commented Mar 28, 2022

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

@ulises-jeremias
Copy link
Author

@praveencastelino I'll check it in a few and let you know if I can reproduce and what is the fix!

@jayrp11
Copy link

jayrp11 commented Mar 28, 2022

@nappalm
Copy link

nappalm commented Nov 22, 2022

Ulises, tienes un ejemplo de como integrar todo este en una App? 🤔

@ulises-jeremias
Copy link
Author

@nappalm ahora mismo no tengo uno pero voy a ver de crear uno dentro de poco!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment