2

I have the following code which check the user_id if available and then log me in but it logs me in only if I refresh the app. Any idea how to make this happen without this?

This is the order of functions:

First when you click the login button from Login.js:

<TouchableOpacity onPress={handleSubmit(_signIn)} style={{margin: 10, alignItems: 'center'}}>

then _signIn function which is in Login.js

_signIn = (values, dispatch) => {
  const email = values.email;
  const password = values.password;
  dispatch(loginUser(email, password));
}

Now we dispatched email and password to loginUser from authActions.js

export function loginUser(email, password) {
  return function (dispatch) {
    return axios.post(SIGNIN_URL, { email, password }).then((response) => {
      var { user_id, token } = response.data;
      onSignIn(user_id); // Here I pass 'user_id' to onSignIn function
    }).catch((error) => {
      dispatch(addAlert("Could not log in."));
    });
  };
}

Now we get the user_id from loginUser inside Auth.js

import { AsyncStorage } from "react-native";

const USER_KEY = "auth_key";

export const onSignIn = (user_id) => AsyncStorage.setItem(USER_KEY, user_id);
export const onSignOut = () => AsyncStorage.removeItem(USER_KEY);

export const isSignedIn = () => {
  return new Promise((resolve, reject) => {
    AsyncStorage.getItem(USER_KEY)
      .then(res => {
        if (res !== null) {
          resolve(true);
        } else {
          resolve(false);
        }
      })
      .catch(err => reject(err));
  });
};

Now in App.js I am calling the function isSignedIn to check if user_id is available and if so will choose which screen to show

 constructor(props) {
    super(props);

    this.state = {
      signedIn: false,
      checkedSignIn: false
    };
  }

  componentDidMount() {
    isSignedIn()
      .then(res => this.setState({ signedIn: res, checkedSignIn: true }))
      .catch(err => alert("An error occurred"));
  }

  render() {
    const { checkedSignIn, signedIn } = this.state;

    // If we haven't checked AsyncStorage yet, don't render anything (better ways to do this)
    if (!checkedSignIn) {
      return null;
    }

    const Layout = createRootNavigator(signedIn);
Markus Hayner
  • 2,869
  • 2
  • 28
  • 60
  • Seems like you should provide the login code. – Cody G Apr 24 '18 at 13:42
  • @CodyG. please see my updated code – Markus Hayner Apr 24 '18 at 13:44
  • It appears to me you're missing `return` in `return onSignIn(user_id);` – Cody G Apr 24 '18 at 13:50
  • @CodyG. Doesn't seem to change anything. – Markus Hayner Apr 24 '18 at 14:14
  • Well, now the next code you should provide is your use of `loginUser` and `isSignedIn` – Cody G Apr 24 '18 at 14:15
  • @CodyG. `loginUser` takes the values email and password then return a user_id and token as you can see. Then the user_id is used in `onSignIn` function and then `isSignedIn` use this to check if the value is there to take me to the next screen or not. – Markus Hayner Apr 24 '18 at 14:27
  • Yeah I think that's where your problem is. psuedo code: `loginUser(values).then(onSignin).then(isSignedIn)` <- somewhere in here you are probably not returning a promise correctly. The next time you log into the app it probably works because it IS performing the function, it's just not waiting for the result correctly before you call `isSignedIn` – Cody G Apr 24 '18 at 14:30
  • 1
    [Your isSignedIn function needs to be rewritten](https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it) – Kos Apr 24 '18 at 15:35
  • Avoid the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! – Bergi Apr 24 '18 at 15:40
  • @CodyG. please review the order of my functions – Markus Hayner Apr 24 '18 at 15:48
  • @MarkusHayner Seems okay, but when you mount the component and then they log in, does it update the state? (as Kos has suggested lower) – Cody G Apr 24 '18 at 18:12
  • @CodyG. It updates the state but I need to call somehow `componentDidMount()` again and have no idea how – Markus Hayner Apr 24 '18 at 18:13

1 Answers1

0

It`s not async function callback issue - I do know how to use it, coz you are already use it in the isSignedIn function.

You did called onSignIn(userId), but you inform nobody about it. Those function, that calls isSignedIn should somehow know about a user logged in.

Based on this issue and previous one, I guess you should choose an architecture of your app (redux or just functional programming or something else) and keep it in mind.
If you wanna use redux, you should dispatch an action about a user logged in and reflect to state change where you need it.

Kirill Glazunov
  • 478
  • 2
  • 10
  • 2
    Agreed. Looks like the problem is that you check `isSignedIn()` when the app mounts - that is, before the user signs in - and don't check it again. State management solution would help here, so that App can be notified when the user gets logged in. – Kos Apr 24 '18 at 15:52
  • @Kos I've checked the order of functions and you are right. Now my question is how do I call `isSignedIn()` from mount after I am passing the `user_id` inside `Auth.js`? – Markus Hayner Apr 24 '18 at 17:30
  • @MarkusHayner it\`s not about an order of functions - it\`s about state management. There is how redux work - it is a state, that get changes by actions and has a subscriptions to the changes. See `react-redux->connect->mapStateToProps` to clarify how to get renewed value to a component from the state. – Kirill Glazunov Apr 25 '18 at 05:24