REACT - Unhandled Rejection (Error): Invalid hook call - javascript

So i am trying to use the useHistory of react-router-dom package using the below code.
function AdminLogin() {
const LoginAct = async () => {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ password: hash })
};
await fetch('/post/admin/login', requestOptions).then(HandleResponse);
}
const HandleResponse = async (response) => {
return response.text()
.then(text => {
if (response.ok) {
var data = text && JSON.parse(text);
data = data[0];
if (data != null) {
LoginRoute();
}
}
})
}
function LoginRoute() {
const history = useHistory();
history.push('/student/app');
}
return (
// View here including button that calls LoginAct when clicked
);
}
export default AdminLogin;
However I am facing this error from const history = useHistory();:
I have tried to debug this with instructions in the URL shown in the error message. No luck!
My react and React DOM versions:
"peerDependencies": {
"react": "^17.0.2"
},
"dependencies": {
"react-dom": "^17.0.2",
},
I have placed the react package to peerDependecies as instructed in some of the answers here!
I have also tested other solutions found from the github issue above for ex. clearing my npm cache and updated all my packages
I have no idea what would be causing this problem other than me breaking Rules of Hooks, in which case I don't know how am I breaking them. (I also have eslint installed to enforce Rules of Hooks but it is possible that I have set it up wrong)

The hook needs to be called at the top level of the component. There's also no reason to abstract the single line into an additional callback, just do the PUSH in the asynchronous handler.
function AdminLogin() {
const history = useHistory(); // <-- here
const LoginAct = async () => {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ password: hash })
};
await fetch('/post/admin/login', requestOptions).then(HandleResponse);
}
const HandleResponse = async (response) => {
return response.text()
.then(text => {
if (response.ok) {
var data = text && JSON.parse(text);
data = data[0];
if (data != null) {
history.push('/student/app'); // <-- called here
}
}
})
}
return (
// View here including button that calls LoginAct when clicked
);
}

Related

SWR not working properly with async fetch

Recently updated SWR - now for some reason my data is not fetching properly.
const { data: expressionsData, error: expressionsError } = useSWRImmutable(
[`dashboard/expression/get-expression-analytics?startTime=${startDate}&endTime=${endDate}`, startDate, endDate],
apiRequest
);
Using this fetching,
import firebase from "./firebase";
export async function apiRequest(path, method = "GET", data) {
const accessToken = firebase.auth().currentUser
? await firebase.auth().currentUser.getIdToken()
: undefined;
//this is a workaround due to the backend responses not being built for this util.
if (path == "dashboard/get-settings") {
return fetch(`/api/${path}`, {
method,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: data ? JSON.stringify(data) : undefined,
})
.then((response) => response.json())
.then((response) => {
if (response.error === "error") {
throw new CustomError(response.code, response.messages);
} else {
return response;
}
});
}
return fetch(`/api/${path}`, {
method,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: data ? JSON.stringify(data) : undefined,
})
.then((response) => response.json())
.then((response) => {
console.log("error", response);
if (response.status === "error") {
// Automatically signout user if accessToken is no longer valid
if (response.code === "auth/invalid-user-token") {
firebase.auth().signOut();
}
throw new CustomError(response.code, response.message);
} else {
return response.data;
}
});
}
// Create an Error with custom message and code
export function CustomError(code, message) {
const error = new Error(message);
error.code = code;
return error;
}
// Check if a indexDb database exists
export function indexedDbdatabaseExists(dbname, callback) {
const req = window.indexedDB.open(dbname);
let existed = true;
req.onsuccess = function () {
req.result.close();
if (!existed) window.indexedDB.deleteDatabase(dbname);
callback(existed);
};
req.onupgradeneeded = function () {
existed = false;
callback(existed);
};
}
Now I'm looking at this StackOverflow thread,
useSWR doesn't work with async fetcher function
And thinking I'll just remake the fetcher to be without Async. I'm just wondering why this has stopped working though in general, and if I can just keep my existing codebase.
The error is a 400 message, it only happens with this expressions API call which takes longer to load due to the amount of data I think,
xxxx/dashboard/expression/get-expression-analytics?startTime=1648183720488&endTime=1650865720488 400 (Bad Request)
with error log
These calls are working fine, they have substantly less data though.
const { data: overall, error: psychometricError } = useSWRImmutable(
`dashboard/psychometric/get-psychometric-home?starttime=infinite`,
apiRequest
);
const { data: sentimentData, error: sentimentError } = useSWRImmutable(
[`dashboard/sentiment/get-sentiment-timefilter?startTime=${startDate}&endTime=${endDate}`, startDate, endDate],
fetchSentiment
);
Made an update to the fetch call to be more readable and specifically about the URL pathway.
import firebase from './firebase';
// Create an Error with custom message and code
export function CustomError(code, message) {
const error = new Error(message);
error.code = code;
return error;
}
export async function expressionsRequest(path, method = 'GET') {
const accessToken = firebase.auth().currentUser
? await firebase.auth().currentUser.getIdToken()
: undefined;
return fetch(`/api/${path}`, {
method,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
})
.then((response) => {
if (!response.ok) {
throw `Server error: [${response.status}] [${response.statusText}] [${response.url}]`;
}
return response.json();
})
.then((receivedJson) => {
if (receivedJson.status === 'error') {
// Automatically signout user if accessToken is no longer valid
if (receivedJson.code === 'auth/invalid-user-token') {
firebase.auth().signOut();
}
throw new CustomError(receivedJson.code, receivedJson.message);
} else {
return receivedJson.data;
}
})
.catch((err) => {
console.debug('Error in fetch', err);
throw err;
});
}
Additionally, this is what the lambda function (using next API folder) looks like,
const requireAuth = require('../../_require-auth');
const { db } = require('../../_sql');
export default requireAuth(async (req, res) => {
const { uid: id } = req.user;
const startTime = Math.round(req.query.startTime * 0.001);
const endTime = Math.round(req.query.endTime * 0.001);
const parameters = [id, startTime, endTime];
//sql injection definitely possible here, need to work out better method of dealing with this.
const sqlText = `SELECT a,b,c,d,e,f,g,h,i FROM tablename WHERE a=$1 AND i BETWEEN $2 AND $3;`;
try {
const { rows } = await db.query(sqlText, parameters);
return res.status(200).json({
code: 0,
data: rows,
});
} catch (error) {
return res.status(200).json({
code: 0,
message: 'Error occurred in getting tablename',
error,
});
}
});
using postman with the same query, i.e.,
curl --location --request GET 'http://localhost:3000/api/dashboard/expression/get-expression-analytics?startTime=1648387240382&endTime=1651069240382' \
--header 'Authorization: Bearer xxxx' \
--data-raw ''
Successfully returns a response with data attached.
Based on your first code blocks, the startDate value is getting passed into the fetcher as method, and the endDate value is getting passed into the fetcher as data. This is based on the useSWR docs about passing in an array for the key argument: https://swr.vercel.app/docs/arguments#multiple-arguments
If the code you provided is correct, I'd assume the 400 is coming from trying to pass in a random value for the method option for fetch.
This should be fixed by only passing the API endpoint path into useSWR instead of an array:
const { data: expressionsData, error: expressionsError } = useSWRImmutable(
`dashboard/expression/get-expression-analytics?startTime=${startDate}&endTime=${endDate}`,
apiRequest
);

Graphql query variable is not updating in reactjs

I am building a simple single-page react website that uses two API's one is rest API and the second is GraphQl using simple hooks
What I am trying to do is:
first fetching the anime_chan data and passing the character name that I get from anime_chan data to GraphQl query search variable so that it gives me the data of that character
The flow I want is -
fetch rest API (anime_chan)
update the search variable in GraphQl query
fetch GraphQl API (ani_list)
set anime_chan_data
set ani_list_info
Problem is:
The GraphQl query search variable is never gets updated
Code
function App(){
const [variables, setVariables] = useState({
search:'lelouch'
});
// I also tried this
/* var variables = {
search: 'lelouch',
} */
console.log('before: '+ variables.search);
var query = `
query($search: String) {
Character(search: $search) {
name {
first
}
image {
large
medium
}
siteUrl
}
}`;
var ani_list_url = 'https://graphql.anilist.co',
options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
query: query,
variables: variables
})
};
const anime_chan_url = 'https://animechan.vercel.app/api/random';
const [anime_chan_data, setAnime_chan_data] = useState({
loading:true,
data:{},
});
const [ani_list_info, setAni_list_info] = useState({});
const get_anime_chan_Data = async() =>{
const response = await fetch(anime_chan_url);
const anime_chan_data = await response.json();
console.log(anime_chan_data);
setAnime_chan_data({
loading:false,
data: anime_chan_data,
});
};
useEffect(() => {
get_anime_chan_Data();
setVariables({
search:anime_chan_data.character
});
console.log('After: '+ variables.search);
fetch(ani_list_url, options).then(handleResponse)
.then(handleData)
.catch(handleError);
function handleResponse(response) {
return response.json().then(function (json) {
return response.ok ? json : Promise.reject(json);
});
}
function handleData(info) {
console.log(info);
setAni_list_info(info);
}
function handleError(error) {
alert('Error, check console');
console.error(error);
}
}, []);
return (
<>...</>
}

Passing query string parameters in Lambda functions (Netlify)

I am trying to pass a query string into my serverless function but it keeps returning an empty object.
search = (searchTerm) => {
// let url = `${URL}${searchTerm}`;
return fetch(`/.netlify/functions/token-hider?search=${searchTerm}`)
.then((response) => response.json())
.then((result) => {
console.log(result.results);
return results;
});
form.addEventListener("submit", (e) => {
e.preventDefault();
let searchTerm = input.value;
search(searchTerm);
});
const axios = require("axios");
const qs = require("qs");
exports.handler = async function (event, context) {
// apply our function to the queryStringParameters and assign it to a variable
const API_PARAMS = qs.stringify(event.queryStringParameters.search);
console.log(event);
// const API_PARAMS = qs.stringify(event.queryStringParameters);
console.log("API_PARAMS", API_PARAMS);
// Get env var values defined in our Netlify site UI
// TODO: customize your URL and API keys set in the Netlify Dashboard
// this is secret too, your frontend won't see this
const { KEY } = process.env;
const URL = `https://api.unsplash.com/search/photos?page=1&per_page=50&client_id=${KEY}&query=${API_PARAMS}`;
console.log("Constructed URL is ...", URL);
try {
const { data } = await axios.get(URL);
// refer to axios docs for other methods if you need them
// for example if you want to POST data:
// axios.post('/user', { firstName: 'Fred' })
return {
statusCode: 200,
body: JSON.stringify(data),
};
} catch (error) {
const { status, statusText, headers, data } = error.response;
return {
statusCode: error.response.status,
body: JSON.stringify({ status, statusText, headers, data }),
};
}
};
it works when i hard code the query string, and i can console log the search term and it is defined.
Since Netlify redirect mechanism is not able to provide you the data of which rule it matched, you could try to match the original request in your function to determine what it should do.
Hope this helps you solve your specific issue!
Here is the reference

axio. to get the data in a different file

I hope you can help me.
I'm trying to get a response from an API and use that information in another file.
I have 3 files:
api.jsx
import axios from 'axios';
export const api = (url, data) => {
const { path, method } = url;
let result ={};
axios({
method: method,
url: path,
data: data
})
.then(res => {
result = res.data;
console.log(res.data);
})
.catch(err => console.error(err));
return result;
};
url.jsx
export const URL = {
users:
{
getAllUsers: { path:'/users', method: 'post'},
login: { path:'/login', method: 'post'},
register: { path:'/register', method: 'post'},
version: { path:'/', method: 'get'},
}
}
app.js (within the render)
const data = {
email: 'hello#world.com',
password: '12345',
};
let result = api(URL.users.login, data);
console.log(result);
In the api file i get the proper response but in the react component no. I am aware that It's a problem of sync as i get first the console of app.jsx and later on the console of the api.jsx but i would like to respect the current structure or make something similar.
Any idea how to fix this without many changes?
PS. sorry about the mess. I tried to highlight all the code but for some reason it is not working fine.
You want to return a Promise in api.jsx
api.jsx
export const api = (url, data) => {
const { path, method } = url
return axios({ // the axios call returns a promise because of its .then; you can just return it
method: method,
url: path,
data: data
})
.then(res => {
return res.data;
})
.catch(err => {
console.error(err)
})
}

Fetch returning TypeError: Type error on iPhone 5

I am having an issue making an API call on the iPhone 5.
I have the following API call made inside a component
import FavouritesService from "../../api/FavouritesService";
const Favourites = () => {
const getFavs = () => {
favouritesService.getFavourites(0, 10, "FULL").then(response => {
if (response.success === false) {
//...
} else {
//...
}
});
};
};
export default Favourites;
My FavouritesService that gets the api endpoint and calls a function inside an import is as follows.
import api from "./api";
class FavouriteService {
getFavourites(page, pageSize, view) {
return api.get(
"/api/social-groups?page=" +
encodeURIComponent(page) +
"&pageSize=" +
encodeURIComponent(pageSize) +
"&view=" +
encodeURIComponent(view) +
"&type=FAVOURITE"
);
}
}
export default FavouriteService;
My api.js file where I do the API itself is as follows...
import _ from "lodash";
import "babel-polyfill";
import "isomorphic-fetch";
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
function handleFetchError(error) {
// Here is where I get TypeError: Type error
if (error) {
return { success: false, error: error };
}
}
function payloadOptions(method, body) {
var postBody = body;
var contentType = "application/x-www-form-urlencoded";
if (typeof postBody === "object") {
postBody = JSON.stringify(postBody);
contentType = "application/json";
}
return {
method: method,
body: postBody,
headers: {
"Content-Type": contentType
}
};
}
const defaultOptions = {
redirect: "error",
headers: {
Accept: "application/json"
}
};
class API {
request(url, options) {
return fetch(url, _.defaultsDeep(options || {}, defaultOptions))
.then(handleErrors)
.then(response => response.json())
.catch(handleFetchError);
}
get(url, options) {
return this.request(
url,
_.defaultsDeep(options || {}, payloadOptions("GET"))
);
}
}
export default new API();
This is where the error occurs in the handleFetchError, error returns TypeError: Type error. When I console.log this, that's all I get, I'm not able to drill down any further to actually inspect what's actually happened here.
I've tried to google this but no one else seems to have this specific issue so I assume I have gone wrong at some step when making this GET request.
Any help would be greatly appreciated as I have been stuck on this for some time.
I found that I had to use a polyfill to allow me to fetch.
This answer helped to figure out I had to install whatwg-fetch

Categories