Can't use the returned Email & Token from AsyncStorage / SecureStore in React Native?

I've searched around for and answer to my problem, but I can't seem to find anything like it. My code is returning a value as expected!

I'm building an app in React-Native and am currently working on the login. I need to save the userId, userEmail, and userToken, and came up with AsyncStorage and Expo's SecureStore as options, but I've got a really odd problem that I've never seen before.

I can set and read the required data, but when I try and use it, it just doesn't work, but when I hardcode it, it does? (it's a little hard to explain).

This is where the problem lies. For ease of reading I have left out the rest of the code, it works just fine if I hardcode the values below...

    // Set the email and token
    // Before setting these, I logged the key's values and (of course), they were null
    // After setting them I logged them out again and they're set

/** from promise (no problems) **/

    SecureStore.setItemAsync('shifterEmail', responseJson.email);
    SecureStore.setItemAsync('shifterToken', responseJson.token);

/** returns response (no problems) **/

...meanwhile, further down the page...

/** another function **/
    let shifterEmail = '';
    let shifterToken = '';

    SecureStore.getItemAsync('shifterEmail')
        .then((email) => {
            shifterEmail = email;
            console.log('API email: ', shifterEmail); // [email protected] <- good!
        })
        .catch((error) => {
            console.log('API email error: ', error);
        });

    SecureStore.getItemAsync('shifterToken')
        .then((token) => {
            shifterToken = token;
            console.log('API token: ', shifterToken); // random token <- good!
        })
        .catch((error) => {
            console.log('API token error: ', error);
        });

    return {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'Authorization': 'Token' +
        ' token=' + shifterToken + // WHAAAT <- doesn't work
        ', email=' + shifterEmail, // WHAAAT <- doesn't work
    };

I had a brainwave and though it might've been good old asynchronicity, and tried nesting them in their callbacks, but it makes no difference. I've shown the code for SecureStore here, but it's the same with AsyncStorage, i can get the expected values for the email and token, but it just doesn't work.

But, here's the odd bit, that I don't understand...

if I hardcode the token and email in the return, it works just fine!

mind = blown

...nothing works :(

On a side note: These are both strings, but I had the same problem with a number, and the only way I got it to work was to bring the 'get' (from both AsyncStorage and SecureStore) out of their callable functions and have the code in-line. Also, if, instead of saving the email and token I just add it to a variable and use that in the return, it works just fine, but this is no good as the user would have to log in every time they open the app.

Totally baffled. Happy to answer any questions, I've tried loads, so we can have a good old chat, or, if you can suggest a different way of handling logging in, I'd love to hear that too!

Why can't I use the value when it's RIGHT THERE, but can hardcode the same thing ???

Thanks!

Upvotes: 1

Views: 1499

Answers (3)

Avanche
Avanche

Reputation: 1810

Really old question, but I just ran into the same issue and think I know the answer.

after getting token from storage, when doing something like:

'token=' + token what actually comes out is: token="sadfaswerw" In other words, the token is wrapped in quotes for some reason. Simple enough to replace the quotes, but a bit odd.

Upvotes: 0

Romario Raffington
Romario Raffington

Reputation: 156

It looks like it's an async issue. Based on the code snippet you have provided the return statement could possibly run before the .then callback is called.

An easy fix could be to use async/await or Promise.all

For example:

Async/Await

const getToken = async () => {
    try{
      const email = await SecureStore.getItemAsync('shifterEmail')
      const token = await SecureStore.getItemAsync('shifterToken')

      return {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'Authorization': 'Token' +
        ' token=' + token + 
        ', email=' + email
      };
    } catch(error) {
      console.error(error);
      throw error;
    }
 }

Promise.all


 const getToken = () => {
    Promise.all([
      SecureStore.getItemAsync('shifterEmail'),
      SecureStore.getItemAsync('shifterToken')  
    ])
   .then((response) => {
      const [email, token] = response
       return {
          'Accept': 'application/json',
          'Content-Type': 'application/json',
          'Authorization': 'Token' +
          ' token=' + token + 
          ', email=' + email
        };
      }
    ).catch((error) => {
      console.error(error);
      throw error;
    });
 }

Upvotes: 1

Cody Swann
Cody Swann

Reputation: 677

The above code is run asynchronously and, as such, the below code in the return statement has not been set yet.

You haven't posted the entire function, but a return statement probably won't work here and you'll need to pass a callback function.

const doSomethingWithLogin = headers =>
  console.log(headers)
  
Promise.all([
  SecureStore.getItemAsync('shifterEmail'),
  SecureStore.getItemAsync('shifterToken')  
])
  .then(([shifterEmail, shifterToken]) =>
    Promise.resolve({
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'Authorization': 'Token' +
      ' token=' + shifterToken + 
      ', email=' + shifterEmail,
    })
  )
  .then(doSomethingWithLogin)

Upvotes: 0

Related Questions