5

I have an actionCreators in my /actions/authenticate.jsto seperate the logic of the actions dispatched by redux and the component of react.

Here is my authenticate.js which is my actionCreators

export function login(email, password) { // Fake authentication function
    return async dispatch => {
        dispatch(loginRequest()); // dispatch a login request to update the state
        try {
            if (email.trim() === "test123@nomail.com" && password === "123456") { //If the email and password matches
                const session = { token: "abc1234", email: email, username: "test123" } // Create a fake token for authentication
                await AsyncStorage.setItem(DATA_SESSION, JSON.stringify(session)) // Stringinfy the session data and store it
                setTimeout(() => { // Add a delay for faking a asynchronous request
                    dispatch(loginSuccess(session)) // Dispatch a successful sign in after 1.5 seconds
                    return Promise.resolve()
                }, 1500)
            } else { // Otherwise display an error to the user
                setTimeout(() => { // Dispatch an error state
                    dispatch(loginFailed("Incorrect email or password"))
                }, 1500)
            }
        } catch (err) { // When something goes wrong
            console.log(err)
            dispatch(loginFailed("Something went wrong"));
            return Promise.reject()
        }
    };
} // login

Then I import that in my someComponent.js to import that actionCreator and bind it using bindActionCreators.

Something like this below:

import { bindActionCreators } from "redux";
import * as authActions from "../actions/authenticate";
import { connect } from "react-redux";

Then I connect that action to my component which is the Login.js

export default connect(
    state => ({ state: state.authenticate }),
    dispatch => ({
        actions: bindActionCreators(authActions, dispatch)
    })
)(Login);

So I can invoke the function in that actionCreator directly in the Login.js

Something like this below:

onPress={() => {                                 
   this.props.actions.login(this.state.email, this.state.password)
}}

But what I want to happen is this function will dispatch an redux action and also return a Promise if that is possible?

Something like this:

onPress={() => {                                 
       this.props.actions.login(this.state.email, this.state.password)
       .then(() => this.props.navigation.navigate('AuthScreen'))
    }}

What I want to happen is when I try to sign in. Dispatch those asynchronous redux thunk actions and as well return a promise. If is been resolved or not so I can redirect or navigate to the correct screen.

Appreciate if someone could help. Thanks in advance.

KnowledgeSeeker
  • 1,058
  • 2
  • 19
  • 44

2 Answers2

6

Your first approach was mostly correct. dispatch(thunkAction()) (or in your case this.props.actions.login() returns whatever thunkAction() returns, so it does return Promise in case it's async.

The problem is what that Promise resolves to, which is whatever you return from the async function. In your case, you don't wait for setTimeout and just return void regardless of whether credentials were correct or not.

So, in terms of async functions you'd need something like

export function login(email, password) { // Fake authentication function
    return async dispatch => {
        dispatch(loginRequest()); // dispatch a login request to update the state
        try {
            if (email.trim() === "test123@nomail.com" && password === "123456") { //If the email and password matches
                const session = { token: "abc1234", email: email, username: "test123" } // Create a fake token for authentication
                await AsyncStorage.setItem(DATA_SESSION, JSON.stringify(session)) // Stringinfy the session data and store it
                // Add a delay for faking a asynchronous
                await new Promise((resolve, reject) => setTimeout(() => resolve(), 1500));
                dispatch(loginSuccess(session));
                return Promise.resolve(true);
            } else { // Otherwise display an error to the user
                await new Promise((resolve, reject) => setTimeout(() => resolve(), 1500));
                dispatch(loginFailed("Incorrect email or password"))
                return Promise.resolve(false);
            }
        } catch (err) { // When something goes wrong
            console.log(err)
            dispatch(loginFailed("Something went wrong"));
            return Promise.reject()
        }
    };
} // login

That way, your async function resolves to true/false that you can use in your component:

onPress={() => {                                 
       this.props.actions.login(this.state.email, this.state.password)
       .then((login_succeeded) => this.props.navigation.navigate('AuthScreen'))
    }}

You can also return dispatch(loginSuccess(session)); (as long as it's thunk that returns it and not the setTimeout handler), in which case loginSuccess(session) is what .then() will get in your onPress hook.

Maciek Wawro
  • 844
  • 6
  • 7
  • This one worked out! Thanks! I was also thinking if should I just past the `this.props.navigation.navigate` into the function and do the navigation logic there. But I think it was a bad idea. Is it? – KnowledgeSeeker Feb 14 '19 at 01:00
  • 1
    You could either do that, or what you're doing now, or maybe [this](https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html). Personally, I'd mostly aim at consistency (either handle navigation from your components, or actions) and otherwise do whatever is more convenient. – Maciek Wawro Feb 14 '19 at 01:29
  • Ok. I think its cleaner. Btw Thank you so much! – KnowledgeSeeker Feb 14 '19 at 01:34
  • Thanks. This is exactly that i want. – Kishan Bharda Jun 21 '19 at 10:45
  • Is it a good practice to pass dispatch to resolve or reject? The approach dispatches for storing and at the same time, you return the payload to the caller. I kinda don't like storing error messages in store as there is no need for it, hence I want it returned as reject. For example: `return Promise.reject( dispatch({type: 'ERROR', error: error}) );`. For resolve, depends on the case. I just like consistency across my action creators regardless if resolve/reject. – duduwe Mar 28 '21 at 14:44
0

In case you need to redirect user to another page depending on login result, you should use Redux Router, then in your login thunk you shoud dispatch an appropriate navigation action which will redirect your user to the correct page.

bnopne
  • 95
  • 7