Firebase auth, React: Auth sporadically failing - reactjs

I have a component GigRegister - one of it's functions is to get all the documents from a collection, and return only the documents created by the currently logged in user:
authListener() {
auth().onAuthStateChanged(user => {
if(user){
this.setState({
userDetails:user
},
() =>
firebase.firestore().collection('gig-listing').onSnapshot(querySnapshot => {
let filteredGigs = querySnapshot.docs.filter(snapshot => {
return snapshot.data().user === this.state.userDetails.uid
})
this.setState({
filterGigs: filteredGigs
})
})
) //end of set state
} else {
this.setState({
userDetails:null
})
console.log('no user signed in')
}
})
}
componentDidMount() {
this.authListener();
}
Another function of this component is to capture data from the user and then post it to firebase, after which it redirects to another component.
handleSubmit(e) {
let user = auth().currentUser.uid;
const gigData = {
name: this.state.name,
venue: this.state.venue,
time: this.state.time,
date: this.state.date,
genre: this.state.genre,
tickets: this.state.tickets,
price: this.state.price,
venueWebsite: this.state.venueWebsite,
bandWebsite: this.state.bandWebsite,
user: user
};
auth()
.currentUser.getIdToken()
.then(function (token) {
axios(
"https://us-central1-gig-fort.cloudfunctions.net/api/createGigListing",
{
method: "POST",
headers: {
"content-type": "application/json",
Authorization: "Bearer " + token,
},
data: gigData,
}
);
})
.then((res) => {
this.props.history.push("/Homepage");
})
.catch((err) => {
console.error(err);
});
}
So here's the issue. Sometimes this component works as it should, and the data submit and redirect work as intended. Occasionally though, I'll hit submit but trigger the message TypeError: Cannot read property 'uid' of null . Interestingly, the post request is still made.
I've been logged in both when it succeeds and fails, and I can only assume that this.state.userDetails.uid evaluating to null means that auth state has expired, or that the component is rendering before userDetails can be assigned a value?
The issue I have is that I can't tell if this is an async problem, or an auth state persistence problem, and I also can't figure why it's a sporadic failure.

This line of code might be causing you trouble:
let user = auth().currentUser.uid;
currentUser will be null if there is no user signed in at the time it was accessed (or it's not known for sure if that is the case). This is covered in the API documentation.
Ideally, you should never use currentUser, and instead rely on the state provided by onAuthStateChanged. I talk about this in detail in this blog post. If you do need to use currentUser, you should check it for null before referencing properties on it.
You should also know that getting an ID token is best done by using a listener as well. The call is onIdTokenChanged, and it works like the auth state listener.
Keep in mind also that setState is asynchronous and doesn't set the state immediately. It's possible that your Firestore query isn't getting the state it needs immediately.

Related

React + Fetch API. Can't set response in setState

I know this has been asked many times on this site but after going through SO questions related to this for the past 5 hours I have to throw in the towel and see if there's someone that can identify what I'm doing wrong here.
I have a fetch request in my react application that I am successfully receiving a response from but I am unable to store the response in my state. It seems to me that everything looks correct but when I attempt to store the response it simply does nothing. There are no console errors in the browser nor in my console that is running the react app. Currently the related code looks like this (Some things are slightly modified for privacy).
loginSubmission = () => {
fetch('https://genericsite.com/auth', {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({"username": this.state.username, "password": this.state.password})
})
.then(res => res.json())
.then(res => {this.setState({response: res}, () => this.sendResponse())})
.catch(error => {
console.log(error);
});
}
sendResponse(){
console.log(this.state.response)
let data = {response: this.state.response};
this.props.receiveResponse(data);
}
If I do it like how I have it below though I'm able to console.log the response with no issues but from what I was reading in a similar question there's something about console.log that forces it to complete the request so it can log the result.
loginSubmission = () => {
fetch('https://genericsite.com/auth', {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({"username": this.state.username, "password": this.state.password})
})
.then(res => res.json())
.then(res => {console.log('res.response'})
.catch(error => {
console.log(error);
});
}
That returns the following object:
{token: 'bigLongJumbledToken', idtoken: '', exp: 1655106045, username: 'myusername'}
exp: 1655106045
idtoken: ""
token: "bigLongJumbledToken"
username: "myusername"
[[Prototype]]: Object
And my state in this component looks like so:
this.state = {
username: '',
password: '',
response: {}
}
this.userOnChange = this.userOnChange.bind(this);
this.passOnChange = this.passOnChange.bind(this);
this.loginSubmission = this.loginSubmission.bind(this);
}
Thanks in advance for any help with this.
In this line: .then(res => {this.setState({response: res}, () => this.sendResponse())}) you are calling the setState with two arguments, it should be only one. I think that you want to store the response in the state and also execute sendResponse function with the response data but, even after you fix the call of setState the function sendResponse will not receive the updated state because react will wait to finish the current executing function that is .then() before actually update the state.
You have two ways of do what (i guess) you are trying to do:
First: use the response directly to call sendResponse
Second: use componentWillUpdate to call sendResponse after state updates
I'll give an example of the first approach cause I think is the cleanest:
.then(res => {
this.setState({response: res})
this.sendResponse(response)
})
sendResponse(res){ // expects response as a parameter
console.log(this.state.response)
// let data = {response: this.state.response}; // avoid this
this.props.receiveResponse(res);
}

Firebase onCreate add custom claims to the auth.user

I am trying to add a custom claims, isRegistered to firebase. My firestore has another user collection to keep register info records. Now I am trying to keep a isRegistered custom claim but I can not seem to get it work.
exports.addRegisteredRole = functions.database.ref('/user')
.onCreate((snap, context) => {
return // **I added this later, but the issue still remains.**
admin.auth()
.setCustomUserClaims(context.auth.uid, {isRegistered: true})
.then(() => {
console.log('done', snap)
return {
message: 'done',
data: snap
}
})
.catch(err => {
console.log('something went wrong', err);
return err
})
});
I am checking this claim by,
currentUser.getIdTokenResult()
.then(res => {
console.log(res.claims.isRegistered)
})
(auth user object). Even if I re-logged it remains undefined. Am I doing something wrong, I am very new to firebase.
The issue is with your onCreate trigger. You assumed you're getting the uid in the context.auth object, which is not correct.
The onCreate trigger will be triggered automatically on the addition of a new document in your "user" collection. In this case, the context.aut.uid is undefined. You should trace this in your function logs.
You can achieve what you are trying to do in a couple of ways
If the user record document has the the user Id as the name of the document, you can do the following
exports.addRegisteredRole =
functions.firestore
.document('test/{docId}')
.onCreate((snap, context) => {
admin.auth()
.setCustomUserClaims(snap.id, { isRegistered: true })
.then(() => {
console.log('done', snap)
return {
message: 'done',
data: snap
}
})
.catch(err => {
console.log('something went wrong', err)
return err
})
})
If you're naming the user record document with something else, or letting firebase decide the name for you, then you must have the uid as an attribute in the document data. Then you use docSnapshot.data().uid instead of docSnapshot.id as follows
exports.addRegisteredRole =
functions.firestore
.document('test/{docId}')
.onCreate((snap, context) => {
admin.auth()
.setCustomUserClaims(snap.data().uid, { isRegistered: true })
.then(() => {
console.log('done', snap)
return {
message: 'done',
data: snap
}
})
.catch(err => {
console.log('something went wrong', err)
return err
})
})
Good luck
I suspect that your Cloud Function gets terminated before the call to Auth completes. The reason for that is that you're not returning anything from the top-level of your code, which means that Cloud Functions assumes that your code is done once the final } runs. But since the call to `` is asynchronous, that call hasn't completed yet and for example your console.log('done', snap) won't have run yet.
exports.addRegisteredRole = functions.database.ref('/user')
.onCreate((snap, context) => {
return admin.auth()
.setCustomUserClaims(context.auth.uid, {isRegistered: true})
.then(() => {
console.log('done', snap)
return {
message: 'done',
data: snap
}
})
.catch(err => {
console.log('something went wrong', err);
return err
})
});
I think you should use functions.firestore.document with wildcard to trigger your "user" documents:
exports.addRegisteredRole = functions.firestore.document('/user/{userId}')
.onCreate((snap, context) => { ... });
Most likely the problem is that you didn't propagate the new custom claims to the client. See here: https://firebase.google.com/docs/auth/admin/custom-claims#propagate_custom_claims_to_the_client
This github issue points that out as well: https://github.com/firebase/firebase-tools-ui/issues/424
If you log out and log back in with the user, it should work. Simply refreshing the page for a logged in user doesn't refresh the user's ID token.
As setting custom claims in the onCreate trigger is a common use case, it would be nice to have a note on this in the docs (https://firebase.google.com/docs/auth/admin/custom-claims) specifically for firestore, as the given example is for the realtime db.

ReactJS - watch access token expiration

In my app, I have an access token (Spotify's) which must be valid at all times. When this access token expires, the app must hit a refresh token endpoint, and fetch another access token, every 60 minutes.
Authorize functions
For security reasons, these 2 calls, to /get_token and /refresh_token are dealt with python, server-side, and states are currently being handled at my Parent App.jsx, like so:
class App extends Component {
constructor() {
super();
this.state = {
users: [],
isAuthenticated: false,
isAuthorizedWithSpotify: false,
spotifyToken: '',
isTokenExpired:false,
isTokenRefreshed:false,
renewing: false,
id: '',
};
componentDidMount() {
this.userId(); //<--- this.getSpotifyToken() is called here, inside then(), after async call;
};
getSpotifyToken(event) {
const options = {
url: `${process.env.REACT_APP_WEB_SERVICE_URL}/get_token/${this.state.id}`,
method: 'get',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${window.localStorage.authToken}`,
}
};
// needed for sending cookies
axios.defaults.withCredentials = true
return axios(options)
.then((res) => {
console.log(res.data)
this.setState({
spotifyToken: res.data.access_token,
isTokenExpired: res.data.token_expired // <--- jwt returns expiration from server
})
// if token has expired, refresh it
if (this.state.isTokenExpired === true){
console.log('Access token was refreshed')
this.refreshSpotifyToken();
}
})
.catch((error) => { console.log(error); });
};
refreshSpotifyToken(event) {
const options = {
url: `${process.env.REACT_APP_WEB_SERVICE_URL}/refresh_token/${this.state.id}`,
method: 'get',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${window.localStorage.authToken}`,
}
};
axios.defaults.withCredentials = true
return axios(options)
.then((res) => {
console.log(res.data)
this.setState({
spotifyToken: res.data.access_token,
isTokenRefreshed: res.data.token_refreshed,
isTokenExpired: false,
isAuthorizedWithSpotify: true
})
})
.catch((error) => { console.log(error); });
};
Then, I pass this.props.spotifyToken to all my Child components, where requests are made with the access token, and it all works fine.
Watcher Function
The problem is that, when the app stays idle on a given page for more than 60 minutes and the user makes a request, this will find the access token expired, and its state will not be updated, so the request will be denied.
In order to solve this, I thought about having, at App.jsx, a watcher function tracking token expiration time on the background, like so:
willTokenExpire = () => {
const accessToken = this.state.spotifyToken;
console.log('access_token in willTokenExpire', accessToken)
const expirationTime = 3600
const token = { accessToken, expirationTime } // { accessToken, expirationTime }
const threshold = 300 // 300s = 5 minute threshold for token expiration
const hasToken = token && token.spotifyToken
const now = (Date.now() / 1000) + threshold
console.log('NOW', now)
if(now > token.expirationTime){this.getSpotifyToken();}
return !hasToken || (now > token.expirationTime)
}
handleCheckToken = () => {
if (this.willTokenExpire()) {
this.setState({ renewing: true })
}
}
and:
shouldComponentUpdate(nextProps, nextState) {
return this.state.renewing !== nextState.renewing
}
componentDidMount() {
this.userId();
this.timeInterval = setInterval(this.handleCheckToken, 20000)
};
Child component
Then, from render() in Parent App.jsx, I would pass handleCheckToken() as a callback function, as well as this.props.spotifyToken, to the child component which might be idle, like so:
<Route exact path='/tracks' render={() => (
<Track
isAuthenticated={this.state.isAuthenticated}
isAuthorizedWithSpotify={this.state.isAuthorizedWithSpotify}
spotifyToken={this.state.spotifyToken}
handleCheckToken={this.handleCheckToken}
userId={this.state.id}
/>
)} />
and in the Child component, I would have:
class Tracks extends Component{
constructor (props) {
super(props);
this.state = {
playlist:[],
youtube_urls:[],
artists:[],
titles:[],
spotifyToken: this.props.spotifyToken
};
};
componentDidMount() {
if (this.props.isAuthenticated) {
this.props.handleCheckToken();
};
};
and a call where the valid, updated spotifyToken is needed, like so:
getTrack(event) {
const {userId} = this.props
const options = {
url: `${process.env.REACT_APP_WEB_SERVICE_URL}/get-tracks/${userId}/${this.props.spotifyToken}`,
method: 'get',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${window.localStorage.authToken}`
}
};
return axios(options)
.then((res) => {
console.log(res.data.message)
})
.catch((error) => { console.log(error); });
};
But this is not working.
At regular intervals, new tokens are being fetched with handleCheckToken, even if I'm idle at Child. But if I make the request after 60 minutes, in Child, this.props.spotifyToken being passed is expired, so props is not being passed down to Child.jsx correctly.
What am I missing?
You are talking about exchanging refreshToken to accessToken mechanism and I think that you over complicated it.
A background, I've a similar setup, login generates an accessToken (valid for 10 mins) and a refreshToken as a cookie on the refreshToken end point (not necessary).
Then all my components are using a simple api service (which is a wrapper around Axios) in order to make ajax requests to the server.
All of my end points are expecting to get a valid accessToken, if it expired, they returns 401 with an expiration message.
My Axios has a response interceptor which check if the response is with status 401 & the special message, if so, it makes a request to the refreshToken endpoint, if the refreshToken is valid (expires after 12 hours) it returns an accessToken, otherwise returns 403.
The interceptor gets the new accessToken and retries (max 3 times) the previous failed request.
The cool think is that in this way, accessToken can be saved in memory (not localStorage, since it is exposed to XSS attack). I save it on my api service, so, no Component handles anything related to tokens at all.
The other cool think is that it is valid for a full page reload as well, because if the user has a valid cookie with a refreshToken, the first api will fail with 401, and the entire mechanism will work, otherwise, it will fail.
// ApiService.js
import Axios from 'axios';
class ApiService {
constructor() {
this.axios = Axios.create();
this.axios.interceptors.response.use(null, this.authInterceptor);
this.get = this.axios.get.bind(this.axios);
this.post = this.axios.post.bind(this.axios);
}
async login(username, password) {
const { accessToken } = await this.axios.post('/api/login', {
username,
password,
});
this.setAccessToken(accessToken);
return accessToken; // return it to the component that invoked it to store in some state
}
async getTrack(userId, spotifyToken) {
return this.axios.get(
`${process.env.REACT_APP_WEB_SERVICE_URL}/get-tracks/${userId}/${spotifyToken}`
);
}
async updateAccessToken() {
const { accessToken } = await this.axios.post(`/api/auth/refresh-token`, {});
this.setAccessToken(accessToken);
}
async authInterceptor(error) {
error.config.retries = error.config.retries || {
count: 0,
};
if (this.isUnAuthorizedError(error) && this.shouldRetry(error.config)) {
await this.updateAccessToken(); // refresh the access token
error.config.retries.count += 1;
return this.axios.rawRequest(error.config); // if succeed re-fetch the original request with the updated accessToken
}
return Promise.reject(error);
}
isUnAuthorizedError(error) {
return error.config && error.response && error.response.status === 401;
}
shouldRetry(config) {
return config.retries.count < 3;
}
setAccessToken(accessToken) {
this.axios.defaults.headers.common.Authorization = `Bearer ${accessToken}`; // assign all requests to use new accessToken
}
}
export const apiService = new ApiService(); // this is a single instance of the service, each import of this file will get it
This mechanism is based on this article
Now with this ApiService you can create a single instance and import it in each Component that whats to make an api request.
import {apiService} from '../ApiService';
class Tracks extends React.Component {
constructor(props) {
super(props);
this.state = {
playlist: [],
youtube_urls: [],
artists: [],
titles: [],
spotifyToken: this.props.spotifyToken,
};
}
async componentDidMount() {
if (this.props.isAuthenticated) {
const {userId, spotifyToken} = this.props;
const tracks = await apiService.getTracks(userId, spotifyToken);
this.setState({tracks});
} else {
this.setState({tracks: []});
}
}
render() {
return null;
}
}
Edit (answers to comments)
Handling of login flow can be done using this service as well, you can extract the accessToken from the login api, set it as a default header and return it to the caller (which may save it in a state for other component logic such as conditional rendering)(updated my snippet).
it is just an example of component which needs to use api.
there is only one instance of the ApiService it is created in the "module" of the file (at the end you can see the new ApiService), after that you just importing this exported instance to all the places that need to make an api call.
If you want to force a rerender of your Child component when the state of the parent component changes, you can use the key prop. Use the spotify token as the key. When the spotify token is re-fetched and updated, it will remount your child component with the new token as well:
<Route exact path='/child' render={() => (
<Child
isAuthenticated={this.state.isAuthenticated}
isAuthorizedWithSpotify={this.state.isAuthorizedWithSpotify}
spotifyToken={this.state.spotifyToken}
key={this.state.spotifyToken}
handleCheckToken={this.handleCheckToken}
userId={this.state.id}
/>
)} />
Though this may reset any internal state that you had in your child component, as it is essentially unmounting and remounting the Child.
Edit: More details
The key prop is a special prop used in React components. React uses keys to determine whether or not a component is unique, from one component to another, or from one render to another. They are typically used when mapping an array to a set of components, but can be used in this context as well. The react docs have an excellent explanation. Basically when the key prop of a component changes, it tells React that this is now a new component, and therefore must be completely rerendered. So when you fetch a new spotifyToken, and assign that new token as the key, React will completely remount the Child with the new props. Hopefully that makes things more clear.
The key prop will not be available from within your Child - this.props.key inside of your child will not be useful to try to access. But in your case, you are passing the same value to the spotifyToken inside the Child, so you'll have the value available there. Its common to use another prop with the same value as key when that value is needed in the child component.
props will not updates on the child, because for a child component they are like immutable options: https://github.com/uberVU/react-guide/blob/master/props-vs-state.md
So you will need some ways to re-render the Child component.
The Child component has already been constructed so will not update and re-render.
So you will need to use "getDerivedStateFromProps()" as a replacement from the deprecated "componentWillReceiveProps" function inside the Child component, so that when receiving updated props from the parent the child will re-render, like this:
class Child extends Component {
state = {
spotifyToken: null,
};
static getDerivedStateFromProps(props, state) {
console.log("updated props", props.spotifyToken);
if (props.spotifyToken !== state.spotifyToken) {
return {
spotifyToken: props.spotifyToken,
};
}
// Return null if the state hasn't changed
return null;
}
getTrack(event) {
const {userId} = this.props
const options = {
url: `${process.env.REACT_APP_WEB_SERVICE_URL}/get-tracks/${userId}/${this.state.spotifyToken}`,
method: 'get',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${window.localStorage.authToken}`
}
};
return axios(options)
.then((res) => {
console.log(res.data.message)
console.log(options.url)
})
.catch((error) => { console.log(error); });
}
};
Note that in the getTrack function you will use the Child state value and not the props.

React object fetched from API is missing properties

I am fetching a profile object from my API following user authentication. The fetch returns the profile object as expected, however my server logger clearly shows a profile object containing an "id" and "username", but the initial object returned to the client has only the "username". I am only able to access the "id" property of the profile abject after I refresh.
Not sure how to fix this, but ive tried everything I can think of...
Login Form
export default class LoginForm extends Component {
static defaultProps = {
onLoginSuccess: () => { }
}
state = { error: null }
handleSubmitJwtAuth = ev => {
ev.preventDefault()
this.setState({ error: null })
const { username, password } = ev.target
//login request
AuthApiService.postLogin({
username: username.value,
password: password.value,
})
//login response
.then(res => {
//updates context profile with username value after login
this.props.updater({ username: username.value })
username.value = ''
password.value = ''
TokenService.saveAuthToken(res.authToken)
this.props.onLoginSuccess()
})
.catch(res => {
this.setState({ error: res.error })
})
}
Profile API Service
const ProfileApiService = {
getProfile() {
return fetch(`${config.API_ENDPOINT}/profile`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `bearer ${TokenService.getAuthToken()}`
}
})
.then(res => {
return (!res.ok)
? res.json().then(e => Promise.reject(e))
: res.json()
}
);
}
}
(API) Profile Service
const ProfileService = {
getProfile : (db,id) =>{
return db
.from('v_users')
.select('id','username')
.where({id})
.first();
},
serializeProfile(profile){
return {
id: profile.id,
username: xss(profile.username)
};
}
}
initially, console.log(this.state.profile.id) //undefined
after a refresh, console.log(this.state.profile.id) // 7
the server log shows this object being returned initially
{ id: 7, username: 'qber83' }, however as mentioned above, I am unable to access the "id" property without refreshing the browser.
The problem here could be that your state is not updated properly, since the object returned is right the API services work, so here your context updater or this.props.onLoginSuccess() might contain the issue.

React - wait for promise before render

I can't figure this out, if you can point me to right direction. On my NavMenu.js I have LogIn (two inputs and a submit button, on which I call handleSubmit()
In handleSubmit() I am checking for user login credentials which works great, but after I confirm login, i procede with doing next fetch for checking user roles (and returning promise) and visibility in application
Helper.js
function getAllRoles(formName, sessionVarName) {
var roleData = [];
var roleId = sessionStorage.getItem('UserLoggedRoleId');
fetch('api/User/GetUserRole/', {
'method': 'POST',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
},
'body':
JSON.stringify({
'roleId': roleId,
'formName': formName
})
}).then(roleResponse => roleResponse.json())
.then(data => {
sessionStorage.setItem(sessionVarName, JSON.stringify(data));
roleData = data;
});
var result = Promise.resolve(roleData)
return result;
}
function checkRoleVisibility(userRoles, role) {} <-- return true or false
export {
getAllRoles ,
checkRoleVisibility
};
In NavMenu.js
import { getAllRoles, checkRoleVisibility } from './common/Helper';
handleSubmit() {
fetch('api/User/UserAuthentification/',
....then(response => {
if (response.ok) {
this.setState(prevState => ({ userLogged: !prevState.userLogged }))
response.json().then(value => {
sessionStorage.setItem('UserLogged', true);
sessionStorage.setItem('UserLabelSession', this.state.userLabel);
sessionStorage.setItem('UserLoggedRoleId', value.userRole.roleID);
getAllRoles('NavMenu', 'UserLoggedRoles')
.then(roleData => {
this.setState({
loggedUserRoles: roleData,
loading: false
});
});
this.props.alert.success("Dobro dosli");
})
}
}
But here comes the problem, is that in render() itself, I have a control navBar which calls
checkRoleVisibility(this.state.loggedUserRoles, 'SeeTabRadniNalozi')
from helper, which send this.state.loggedUserRoles as parameter, which suppose to be filled in fetch from handleSubmit() but it's undefined, fetch finish, set loading on true, rendering completes and it doesn't show anything
I have this.state.loading and based on it I show control ...
let navMenu = this.state.loading ? <div className="loaderPosition">
<Loader type="Watch" color="#1082F3" height="200" width="200" />
</div> : !this.state.userLogged ? logInControl : navBar;
If I put a breakpoint on controler I can see my method is being called by getAllRoles function, but also I can see that application keep rendering on going and doesn't wait for promise to return to fill state of loggedUserRoles.
So question is, why rendering doesn't wait for my role fetch nor doesn't wait for promise to resolve before setting loading on true?
I checked this answer Wait for react-promise to resolve before render and I put this.setState({ loading: false }); on componentDidMount() but then loading div is shown full time
From what I see and understand is, that you're calling
this.setState(prevState => ({ userLogged: !prevState.userLogged }))
if the response is okay. This will lead in an asynchronous call from React to set the new State whenever it has time for it.
But you need the resource from
this.setState({
loggedUserRoles: roleData,
loading: false
});
whenever you want to set userLogged to true, right?
So it might be that React calls render before you set the loggedUserRoles but after userLogged is set to true. That would result in an error if I understand you correctly.
You could simply set the userLogged state with the others together like this:
this.setState({
userLogged: true,
loggedUserRoles: roleData,
loading: false
});
EDIT:
Moreover you want to change your Promise creation inside of getAllRoles. You're returning
var result = Promise.resolve(roleData)
return result;
This will directly lead to the empty array since this is the initial value of roleData. Therefore you're always returning an empty array from that method and don't bother about the fetched result.
You've multiple ways to solve this. I would prefer the clean and readable way of using async/await:
async getAllRoles(formName, sessionVarName){
var roleId = sessionStorage.getItem('UserLoggedRoleId');
const response = await fetch(...);
const data = response.json();
sessionStorage.setItem(sessionVarName, JSON.stringify(data));
return data;
Or if you want to stay with Promise:
function getAllRoles(formName, sessionVarName){
return new Promise((resolve, reject) => {
var roleId = sessionStorage.getItem('UserLoggedRoleId');
fetch(...)
.then(roleResponse => roleResponse.json())
.then(data => {
sessionStorage.setItem(sessionVarName, JSON.stringify(data));
resolve(data);
});

Resources