Jim
Jim

Reputation: 2312

How to create an action with arguments that returns a promise?

Goal: create an action that returns a promise, that takes arguments, and utilizes dispatch.

the best I've come up with for the action:

export const loginAction = (arg1, arg2) => {
    return (dispatch) => {
      dispatch({type: types.SOME_TYPE});
      return new Promise((resolve, reject) => {
        firebase.auth().onAuthStateChanged((user) => {
          if (user) {
            loginSuccess(dispatch, user);
            resolve(user)
          }
        }).catch(error => reject(error))
      })
    }   
}

the way I want to implement it:

login = () => {
  this.props.loginAction(this.state.email, this.state.password)
    .then(user => {
      if (user) {
        this.props.navigation.navigate("App")
      }
    }
}

LoginScreen:

import React from 'react';
import { loginUser, logoutUser, resetAuth, sendPwdLink } from '../actions';
import { connect } from 'react-redux';
// other imports...
const { width } = Dimensions.get('window');

class LoginScreen extends React.Component {
    constructor (props) {
        super(props);
        this.state = {
            email: '',
            password: ''
        };
    }
    goToRegister = () => {
        this.props.resetAuth();
        this.props.navigation.navigate('Register');
    };
    goToLearnMore = () => {
        this.props.navigation.navigate('LearnMore');
    };
    login = () => {
        this.props.loginUser(this.state.email, this.state.password).then((user) => {
            user ? this.props.navigation.navigate('App') : null;
        });
    };
    render () {
        return (
            <View style={styles.container}>

                <View style={{ alignItems: 'center' }}>
                    <Components.TransparentInput
                        placeholder={'Email Address...'}
                        style={{ width: width * 0.5, margin: width * 0.015, fontFamily: Fonts.QUICKSAND }}
                        placeholderColor={'white'}
                        onChangeText={(text) => {
                            this.setState({ email: text });
                        }}
                    />
                    <Components.TransparentInput
                        placeholder={'Password...'}
                        secureTextEntry={true}
                        style={{ width: width * 0.5, margin: width * 0.015, fontFamily: Fonts.QUICKSAND }}
                        placeholderColor={'white'}
                        onChangeText={(text) => {
                            this.setState({ password: text });
                        }}
                    />
                    <TouchableWithoutFeedback onPress={() => this.sendPwdLink(this.state.email)}>
                        <Text
                            style={{
                                fontSize: 14,
                                color: 'white',
                                textDecorationLine: 'underline',
                                fontFamily: Fonts.QUICKSAND
                            }}
                        >
                            Forgot password?
                        </Text>
                    </TouchableWithoutFeedback>
                </View>

                <View>
                    <Components.Button
                        type={'primary'}
                        text={'Login'}
                        onPress={this.login}
                        style={{ width: width * 0.4, margin: width * 0.015 }}
                        fontSize={18}
                    />
                    <Components.Button
                        style={{ width: width * 0.35, margin: width * 0.015 }}
                        type={'secondary'}
                        text={'Register'}
                        onPress={this.goToRegister}
                        fontSize={18}
                    />
                </View>

                <TouchableWithoutFeedback onPress={this.goToLearnMore}>
                    <Text
                        style={{
                            fontSize: 16,
                            color: 'white',
                            textDecorationLine: 'underline',
                            fontFamily: Fonts.QUICKSAND
                        }}
                    >
                        What is Slide?
                    </Text>
                </TouchableWithoutFeedback>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'space-evenly',
        alignItems: 'center',
        backgroundColor: Colors.PRIMARY_DARK
    }
});

const mapStateToProps = ({ auth }) => {
    const { authenticated, loading, error, user } = auth;
    return { authenticated, loading, error, user };
};

const mapDispatchToProps = { loginUser, logoutUser, resetAuth, sendPwdLink };

export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen);

But this doesn't work, it results in an error: TypeError: undefined is not an object...evaluating this.props.loginAction(arg1, arg2)

EDIT: I believe the actions have access to dispatch through the mapDispatchToProps function. I could be wrong. I dont totally understand that part of the action function but it didnt work without it.

What changes can be made to fix this?

Upvotes: 0

Views: 84

Answers (1)

alex2007v
alex2007v

Reputation: 1300

[EDITED]

First of all you need redux-thunk.

In place where you create store you need to add this.

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';

const store = createStore(rootReducer, applyMiddleware(thunk));

your loginAction is correct.

Your just need to update mapDispatchToProps

import { bindActionCreators } from 'redux'; // dont forget to import this

const mapDispatchToProps = dispatch => 
  bindActionCreators({
    loginUser,
    logoutUser,
    resetAuth,
    sendPwdLink,
  }, dispatch);

bindActionCreators - Turns an object whose values are action creators, into an object with the same keys, but with every action creator wrapped into a dispatch call so they may be invoked directly.

so bindActionCreators does next:

const actions = { loginUser };
const bindActions = Object.keys(actions).map((action) => dispatch(action));

link to proof

next when you call in your code your function which is loginUser it dispatches your function which comes to redux-thunk middleware and after redux-thunk check it runs your function with next arguments:

your_action_function(dispatch, getState, extraArgument);

I hope now it's clear for you

Upvotes: 1

Related Questions