Search code examples
reactjsreact-nativereduxdispatch

React-Native with Redux Cannot read property 'dispatch' of undefined


I want to call an action from my componentWillMount() after the firebase auth manager says there is no authenticated user. This is my code:

import React, { Component } from 'react';
import { View, Text, Image, StyleSheet } from 'react-native';
import { Button } from 'react-native-elements';
import { Actions } from 'react-native-router-flux';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import * as actions from '../actions';
import * as firebase from 'firebase';
import { responsiveHeight, responsiveWidth, responsiveFontSize } from 'react-native-responsive-dimensions';

const ristImage = require('../assets/risto-landing.png');

class LandingScreen extends Component {

  componentWillMount(){
    firebase.auth().onAuthStateChanged(function(user) {
   if (user) {
     console.log('User Logged-in')
   } else {
     console.log('No Authenticated User')
     return(
  this.props.dispatch(clearState())
    )
   }
 });
  }

  fbLoginPress() {
  this.props.fbLogin();
  }

  render() {
    return (
      <View style={styles.containerStyle}>
        <View>
          <Image
            resizeMode="contain"
            style={styles.landingImageStyle}
            source={ristImage}
          />
          <Text style={styles.titleAppStyle}>Risto </Text>
        </View>

        <View style={styles.buttonContainer}>
          <Button
            large
            title="Sign in With Facebook"
            backgroundColor="#4068AD"
            icon={{ name: 'facebook-square', type: 'font-awesome' }}
            style={[styles.buttonStyle, styles.fbColor]}
            onPress={this.fbLoginPress.bind(this)}
          />

          <Button
            large
            title="Sign In or Sign Up With Email"
            icon={{ name: 'envelope', type: 'font-awesome' }}
            backgroundColor="#F8A443"
            style={[styles.buttonStyle, styles.emailColor]}
            onPress={Actions.emailLogin}
          />

        </View>
      </View>
    );
  }
}

export default connect(null, actions)(LandingScreen);

Now, what I want is to call my action clearState() from my ./authAction files to clear back the initial state. Is my approach correct or is it also possible to call the dispatch action directly within my componentWillMount?

Then this is where my clearState() functions in actions/js:

export const clearState = () => async (dispatch) => {
  dispatch({type: SIGN_OUT});
  console.log('State re-initialized');
}

Solution

  • The this in the callback you provide to onAuthStateChanged() is not what you think it is. It is bound to something else that has not property named props. That's why you get that error. You can do two things to avoid that:

    1. Use an arrow function:

    firebase.auth().onAuthStateChanged(user => {
        ...
    }
    

    2. bind the callback to your this.

    firebase.auth().onAuthStateChanged((function(user) {
        ...
    }).bind(this);
    

    I prefer the first alternative.

    Also if you pass an object of actions to your connect() function it will not inject dispatch into your props. Instead it will provide a function as a prop with the same name and already wrapped into a dispatch().

    From the react-redux docs:

    [mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function): If an object is passed, each function inside it is assumed to be a Redux action creator. An object with the same function names, but with every action creator wrapped into a dispatch call so they may be invoked directly, will be merged into the component’s props.

    That means you just have to call this.props.clearState().