Storing a value in Redux

Viewed 3370

I am building a React Native app, mainly for verifying tickets, to be used by event administrators. The back-end is served by a Laravel app with a working OAuth2-server. I have a working login against that server but now I need to store the access token, to request data, such as events, and to verify if a ticket is matched for a given event.

I'm trying to implement Redux to store the access token etc. The login form I have updates the store via actions correctly, but I can't get it to work with the access token.

Here is the login screen:

import React, { Component } from 'react';
import { Text, View, TextInput, Button } from 'react-native';
import { connect } from 'react-redux'
import StringifyBody from './../lib/oauth2/StringifyBody'
import { login, storeTokens } from '../redux/actions/auth.js'

class Login extends Component {
  constructor (props) {
      super(props);
      this.state = {
          route: 'Login',
          loading: false,
          email: '',
          password: '',
          accessToken: '',
      };
  }

  handleClick (e) {
    e.preventDefault();

    return new Promise(function(resolve, reject) {
    var data = StringifyBody(this.state.password, this.state.email)

        // XHR settings
        var xhr = new XMLHttpRequest()
        xhr.withCredentials = true

        xhr.onerror = function() {
            reject(Error('There was a network error.'))
        }

        xhr.open("POST", "http://192.168.0.141/oauth/access_token")
        xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded")

        xhr.send(data)

        xhr.onloadend = function() {

            if (xhr.status === 200) {

                var parsedJson = JSON.parse(xhr.response)

                responseArray = []

                for(var i in parsedJson) {
                    responseArray.push([parsedJson [i]])
                }

                // assign values to appropriate variables
                let accessToken = responseArray[0];

                console.log('access token is: ' + accessToken)

                accessToken => this.setState({ access_token: accessToken })

                this.props.tokenStore(this.state.accessToken)  // This doesn't work: "cannot read property 'tokenStore' of undefined"

                resolve(xhr.response)

            } else {
                reject(Error('Whoops! something went wrong. Error: ' + xhr.statusText))
            }
        }
    })
    .done(() => {
        this.props.onLogin(this.state.email, this.state.password); // This works 
    })
}
  render() {

    return (
        <View style={{padding: 20}}>
            <Text style={{fontSize: 27}}>{this.state.route}</Text>
            <TextInput 
                placeholder='Email'
                autoCapitalize='none'
                autoCorrect={false} 
                keyboardType='email-address'
                value={this.state.email} 
                onChangeText={(value) => this.setState({ email: value })} />
            <TextInput 
                placeholder='Password'
                autoCapitalize='none'
                autoCorrect={false} 
                secureTextEntry={true} 
                value={this.state.password} 
                onChangeText={(value) => this.setState({ password: value })} />
            <View style={{margin: 7}}/>
            <Button onPress={(e) => this.handleClick(e)} title={this.state.route}/>
        </View>
    );
}
}

const mapStateToProps = state => {
  return { 
    isLoggedIn: state.auth.isLoggedIn,
    access_token: state.auth.access_token, 
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onLogin: (email, password) => { dispatch(login(email, password)); },
    tokenStore: (accessToken) => { dispatch(storeTokens(accessToken)) },
  }
}

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

Redux actions:

export const login = (email, password) => {
  return {
    type: 'LOGIN',
    email: email,
    password: password
  };
};

export const logout = () => {
  return {
    type: 'LOGOUT'
  };
};

export const storeTokens = () => {
  return {
    type: 'STORE_TOKENS',
    access_token: accessToken,
  }
}

And finally the reducers:

const defaultState = {
  isLoggedIn: false,
  email: '',
  password: '',
  access_token: '',
};

export default function reducer(state = defaultState, action) {
  switch (action.type) {
    case 'LOGIN': 
        return Object.assign({}, state, { 
            isLoggedIn: true,
            email: action.email,
            password: action.password
        });
    case 'LOGOUT':
        return Object.assign({}, state, { 
            isLoggedIn: false,
            email: '',
            password: ''
        });
    case 'STORE_TOKENS':
        return Object.assign({}, state, {
            access_token: action.accessToken,
        })
    default:
        return state;
  }
}

I've also tried passing the data to this.props.storeTokens (the actual action) in a componentDidMount() which gives me the error undefined is not a function (evaluating 'this.props.storeTokens()') componentDidMount Login.js:57:8

My question then is: How do I store the variable I get from my XHR POST in the redux store? Why is this.props.tokenStore and this.props.storeToken not defined?

1 Answers

Hey thats a mistake owing to javascript concept. You are calling this.props.tokenStore(this..state.accessToken) // This doesn't work: "cannot read property 'tokenStore' of undefined"

inside a function defined using ES5 syntax. either you store the reference of this outside the function in some variable and then use that variable instead of this. The other option is define arrow function instead. So change your function keyword into () => and this should work. this as of now in your implementation doesn't point to component that you are thinking

Related