React useState hook not working as I would expect - javascript

I'm using the useState hook in React and it's behaving in an odd way.
If you look at the example below here is what I would expect: call login, on success it calls setRefreshToken(responseToken) then calls refresh() which references refreshToken set from setRefreshToken. What actually happens is refreshToken is undefined inside of refresh().
I know setState is async but I haven't run in to issues like this before. Am I missing something?
import React, { createContext, useState } from "react";
import jwtDecode from "jwt-decode";
const localStorageKey = "ar_refresh_token";
export const AuthContext = createContext();
export function AuthProvider({ tokenUrl, registerUrl, refreshUrl, children }) {
const [refreshToken, setRefreshToken] = useState(
window.localStorage.getItem(localStorageKey)
);
const [accessToken, setAccessToken] = useState();
const login = async (userId, password) => {
const response = await fetch(tokenUrl, {
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
userId,
password
})
});
if (response.status === 201) {
const token = await response.text();
setRefreshToken(token);
window.localStorage.setItem(localStorageKey, token);
await refresh();
}
};
const refresh = async () => {
const response = await fetch(refreshUrl, {
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
Authorization: `JWT ${refreshToken}`
}
});
if (response.status === 201) {
const token = await response.text();
setAccessToken(token);
}
};
return (
<AuthContext.Provider
value={{
refreshToken,
accessToken,
login,
refresh
}}
>
{children}
</AuthContext.Provider>
);
}
Full example: https://github.com/analyticsrequired/auth-admin/blob/master/src/AuthContext.js

You are right in that the component will not have been re-rendered before you call refresh, so the refreshToken inside refresh will be the default one.
You could instead pass in the token from login as an argument to refresh and use that and it will work as expected.
const login = async (userId, password) => {
// ...
if (response.status === 201) {
// ...
await refresh(token);
}
};
const refresh = async refreshToken => {
const response = await fetch(refreshUrl, {
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
Authorization: `JWT ${refreshToken}`
}
});
// ...
};

You can use useEffect to automatically call refresh whenever refreshToken changes:
...
if (response.status === 201) {
const token = await response.text();
setRefreshToken(token);
window.localStorage.setItem(localStorageKey, token);
//await refresh();
}
...
useEffect(
() => {
// your refresh code
},
[refreshToken]
)
The code inside useEffect will be called on the first render and after refreshToken is changed. So you don't need to call refresh after every call to setRefreshToken.

Related

async await resolve graphql query promise not resolved

Hi All i am trying to use graphql api to fetch some country. I am using this API https://studio.apollographql.com/public/countries/home?variant=current and this is playground https://countries.trevorblades.com/
The problem i am facing is that i cannot resolve the Promise. I am using React and fetch method.
my fetchCountries.js file:
import { API_URL } from "./constants";
const COUNTRIES_QUERY = `{
country(code: "BR") {
name
native
emoji
currency
languages {
code
name
}
}
}`;
const fetchCountries = async () => {
await fetch(API_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: COUNTRIES_QUERY,
}),
})
.then((response) => {
if (response.status >= 400) {
throw new Error("Error fetching data");
} else {
return response.json();
}
})
.then((data) => data.data);
};
export default fetchCountries;
i am using async, await in the function so that it gets resolved when i call it. Now in my App.js when i call fetchCountries function in console.log i get :
Promise {<pending>}
[[Prototype]]
:
Promise
[[PromiseState]]
:
"fulfilled"
[[PromiseResult]]
:
undefined
App.js:
import "./App.css";
import fetchCountries from "./api/fetchCountries";
const App = () => {
console.log("CONSOLE LOG API", fetchCountries());
return <div>App</div>;
};
export default App;
I have also tried to use useEffect hook but got the same result:
import "./App.css";
import fetchCountries from "./api/fetchCountries";
import React, { useEffect, useState } from "react";
const App = () => {
const [test, setTest] = useState();
console.log("CONSOLE LOG API", fetchCountries());
useEffect(() => {
const getCountries = async () => {
await fetchCountries();
};
setTest(getCountries());
}, []);
console.log("USE EFFECT API", test);
return <div>App</div>;
};
export default App;
You should do it as follows
const fetchCountries = async () => {
const response = await fetch(API_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: COUNTRIES_QUERY,
}),
})
if (response.status >= 400) {
throw new Error("Error fetching data");
} else {
return await response.json();
}
};
export default fetchCountries;
Or enclose entire fetch.then... chain inside paranthesis.

How to get the raw string of cookie using js-cookie

I am trying to use js-cookie to store access_token as cookies in the cookie strorage. But whenever I set the cookie, it gets displayed like this: %22eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjU5NDIzOTA3LCJpYXQiOjE2NTg1NTk5MDcsImp0aSI6IjAyMmRlMTg2MTk0NzRhYzY5ZThjOTVmOGNhZDE3OTM4IiwidXNlcl9pZCI6Mn0.xkOTI4uO93_YIvlGrcnnKLFjbLZiM_RdNo9J9A8Clk4%22
But I dont want it that way. I weant to get the token without "%22" at the beginning and end.
This is my code. Thank you.
import {createContext, useState, useEffect} from 'react'
import Cookie from "js-cookie";
import jwt_decode from "jwt-decode"
import {useHistory} from 'react-router-dom'
const SetCookie = (cookiename, cookie) => {
Cookie.set(cookiename, cookie)
}
const GetCookie = (cookiename) => {
return Cookie.get(cookiename)
}
const AuthContext = createContext()
export default AuthContext
export const AuthProvider = ({children}) => {
let [authTokens, setAuthTokens] = useState(localStorage.getItem('authTokens') ? JSON.parse(localStorage.getItem('authTokens')):null)
let [user, setUser] = useState(localStorage.getItem('authTokens') ? JSON.parse(localStorage.getItem('authTokens')):null)
const histroy = useHistory()
histroy.push('/')
let loginUser = async (e)=> {
e.preventDefault()
let response = await fetch('http://127.0.0.1:8000/users/login/', {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
body:JSON.stringify({'email':e.target.email.value, 'password':e.target.password.value})
})
let data = await response.json()
console.log('response:', response)
if (response.status === 200) {
setAuthTokens(data)
setUser(data)
SetCookie('cookie', JSON.stringify(data.access_token))
localStorage.setItem('authTokens', JSON.stringify(data))
} else{
alert('there was problem signing in')
}
}
let registerUser = async (e)=> {
e.preventDefault()
let response = await fetch('http://127.0.0.1:8000/users/register/', {
method: 'POST',
headers:{
'Content-Type':'application/json'
},
body:JSON.stringify({'username':e.target.username.value, 'email':e.target.email.value, 'password1':e.target.password1.value, 'password2':e.target.password2.value})
})
let data = await response.json()
console.log('response:', response)
if (response.status === 201) {
alert('An email verification message has been sent to your email address')
} else{
alert('Something went wrong! Check your credentials and try again')
}
}
let passwordReset = async (e)=> {
e.preventDefault()
let response = await fetch('http://127.0.0.1:8000/password-reset/', {
method: 'POST',
headers:{
'Content-Type':'application/json'
},
body:JSON.stringify({'email':e.target.email.value})
})
let data = await response.json()
console.log('response:', response)
}
let passwordresetConfirm = async (e)=> {
e.preventDefault()
let response = await fetch('http://127.0.0.1:8000/password-reset-confirm/', {
method: 'POST',
headers:{
'Content-Type':'application/json'
},
body:JSON.stringify({'new_password1':e.target.new_password1.value, 'new_password2':e.target.new_password2.value,})
})
let data = await response.json()
console.log('response:', response)
}
let logoutUser = () => {
setAuthTokens(null)
setUser(null)
localStorage.removeItem('authTokens')
histroy.push('/login')
}
let contextData = {
user:user,
loginUser : loginUser,
logoutUser : logoutUser,
registerUser : registerUser,
passwordReset : passwordReset,
passwordresetConfirm : passwordresetConfirm,
}
return(
<AuthContext.Provider value={contextData}>
{children}
</AuthContext.Provider>
)
}

How do I return an item from an async funtion

Ive been looking into trying to get information from websites using fetch. Trying async/await was my first solution, and so far it has been going well. Getting the information I needed was a breeze, but trying to retrieve that data in main is my problem. Const data in my main function even with the await tag only returns an undefined object. How can I get the data that I have in the function into my main
import React from "react";
import {
WASTEMANAGEMENT_USERNAME,
WASTEMANAGEMENT_PASSWORD,
WASTEMANAGEMENT_APIKEY_AUTH,
WASTEMANAGEMENT_APIKEY_CUSTID,
WASTEMANAGEMENT_APIKEY_DATA,
} from "#env";
async function login() {
let response = await fetch("https://rest-api.wm.com/user/authenticate", {
method: "POST",
headers: {
"content-type": "application/json",
apikey: WASTEMANAGEMENT_APIKEY_AUTH,
},
body: JSON.stringify({
username: WASTEMANAGEMENT_USERNAME,
password: WASTEMANAGEMENT_PASSWORD,
locale: "en_US",
}),
});
let res = await response.json();
const id = res.data.id;
const access_token = res.data.access_token;
response = await fetch(
`https://rest-api.wm.com/authorize/user/${id}/accounts?lang=en_US`,
{
method: "GET",
headers: {
oktatoken: access_token,
apikey: WASTEMANAGEMENT_APIKEY_CUSTID,
},
}
);
res = await response.json();
const custId = res.data.linkedAccounts[0].custAccountId;
response = await fetch(
`https://rest-api.wm.com/account/${custId}/invoice?userId=${id}`,
{
method: "GET",
headers: {
apikey: WASTEMANAGEMENT_APIKEY_DATA,
token: access_token,
},
}
);
res = await response.json();
res.body.balances.filter((item) => {
if (item.type === "Current") {
console.log(item);
return item;
}
});
}
const WasteManagementLogin = async () => {
const data = await login();
console.log(data);
};
export default WasteManagementLogin;

How to fetch multi data in useEffect

in my react application i want to use multi fetch in useEffect but, they not working just first function works!.
code:
useEffect(() => {
const getInfo = async () => {
try {
const user = await transitionData(
`http://localhost:5000/user/${authContext.userId}/profile?t=user`,
"GET",
null,
{ Authorization: `Bearer ${authContext.token}` }
);
const posts = await transitionData(
`http://localhost:5000/user/${authContext.userId}/profile?t=posts`,
"GET",
null,
{ Authorization: `Bearer ${authContext.token}` }
);
const savedPosts = await transitionData(
`http://localhost:5000/user/${authContext.userId}/profile?t=saved`,
"GET",
null,
{ Authorization: `Bearer ${authContext.token}` }
);
setUser(user);
setPosts(posts);
setSaved(savedPosts);
} catch (error) {}
};
getInfo();
}, [transitionData, authContext.userId, authContext.token]);

AsyncStorage await issue with redux and axios

I need to send an axios request passing a token, this token is saved on my AsyncStorage. The problem is, when i make the call looks like its been sended without the AsyncStorage return the token, also the then/catch do not trigger.
Code example:
export const verificarPreco = (produto, estabelecimento) => {
return async dispatch => {
axios({
method: "get",
url: `${API}preco/verificarPreco/?produto=${produto}&estabelecimento=${estabelecimento}`,
headers: {
"x-access-token": await AsyncStorage.getItem("#Offer4All:token")
}
})
.then(response => {
verificarPrecoSucesso(response.data, dispatch);
})
.catch(error => {
verificarPrecoErro(error.response, dispatch);
});
};
};
You could use just async/await instead of handling the promises manually, and putting a try/catch around that.
export const verificarPreco = (produto, estabelecimento) => {
return async dispatch => {
try {
const token = await AsyncStorage.getItem("#Offer4All:token");
const request = await axios({
method: "get",
url: `${API}preco/verificarPreco/?produto=${produto}&estabelecimento=${estabelecimento}`,
headers: {
"x-access-token": token
}
});
const response = await request.json();
verificarPrecoSucesso(response.data, dispatch);
} catch (error) {
verificarPrecoErro(error.response, dispatch);
}
};
};

Categories

Resources