1
votes

So I'm new to React-Native and also to Redux. I was integrating Redux into React-Native and everything worked really well, with the exception of dispatching actions. Everytime I'm trying to dispatch actions, i get the error' Redux Uncaught Error: Actions must be plain objects. Use custom middleware for async actions'. I even tried to integrate react-thunk, but without success. I even tried dispatching directly on the store with store.dispatch(setIsLoggedIn(true)). My application should support a login function and I wanted to set the state, so that I know, if my user is logged in or not.

My store is initiliazed like this:

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

const storeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(rootReducer, storeEnhancers(applyMiddleware(thunk)));

export default store;

My reducers like this:

import {SET_IS_LOGGED_IN, SET_USER, SET_TOKEN} from '../actions/index'

const initialState =
{
    loggedIn: false,
    user: null,
    token: "",
}
//slice zum removen
function rootReducer(state=initialState, action)
{
    switch (action.type) {
        case SET_IS_LOGGED_IN:
          return Object.assign({}, state, 
            {
              loggedIn: true
            })
          break;
          case SET_USER:
        return Object.assign({}, state, 
          {
              user: action.user
          })
        break;
        case SET_TOKEN:
        return Object.assign({}, state, 
          {
            token: action.token
          })
        break;
        default:
          // Anweisungen werden ausgeführt,
          // falls keine der case-Klauseln mit expression übereinstimmt
          break;
      }
    return state;
}

export default rootReducer;

My actions are defined like this:

export const SET_IS_LOGGED_IN='SET_IS_LOGGED_IN'

export function setIsLoggedIn(value)
{
    return
    {
        type: SET_IS_LOGGED_IN,
        value
    };
}

export function setIsLoggedInAsync(value)
{
    return dispatch =>
    {
        setTimeout(() => 
        {
            dispatch(setIsLoggedIn(value))
        },1000);
    };
}

export const SET_USER='SET_USER'
export function setUser(user)
{
    return
    {
        type: SET_USER,
        user
    };
}

export const SET_TOKEN='SET_TOKEN'

export function setToken(token)
{
    return
    {
        type: SET_TOKEN,
        token
    };
}

And my main component like this:

import React, { Component } from 'react';
import {ScrollView, Text, TextInput, View, Button, StyleSheet, Image, TouchableOpacity, Linking}  from 'react-native';
import UserLogin from '../model/UserLogin';
import {loginCall} from '../api/User-Api';
import Logo from '../../assets/logo.png';
import {connect} from 'react-redux';
import {setIsLoggedIn, setUser, setToken} from '../redux/actions/index'
import store from '../redux/store/index'

const mapStateToProps=(state)=> (
{
        test: state.test 
} 
)

const mapDispatchToProps = (dispatch) => (

    {
        setIsLoggedIn: value => dispatch(setIsLoggedIn(true)),
        setUser: user => dispatch(setUser(user)),
        setToken: token => dispatch(setToken(token)),
    }
)

class Login extends Component {

    constructor(props){
        super(props);

        this.state = {
            email: "null",
            password:"null",
            wrongPw : false,
            title: "bla"
          }

        this._onPressButton = this._onPressButton.bind(this);
        this._onRegisterButton = this._onRegisterButton.bind(this);

    }

    componentDidMount()
    {
        console.log(store.getState());
    }

    componentWillUnmount()
    {
        console.log(store.getState());
    }

    _onRegisterButton()
    {
        var link = "";
        Linking.canOpenURL(link).then(supported => {
            if (supported) {
              Linking.openURL(link);
            } else {
              console.log("Don't know how to open URI: " + this.props.url);
            }
          });
    }


    _onPressButtonTest() 
    {
        store.dispatch(setIsLoggedIn(true))
    }

    _onPressButton() {
        //let username = this.state.email;
        this.setState({wrongPw: false});
        var user = new UserLogin();
        user.email = this.state.email;
        user.password = this.state.password;
        loginCall(user).then((response) => 
            {
                console.log(response);
                // Set token for api calls const token = response.data;
                if(response.status == 200)
                {   
                    console.log(response.data)
                    this.props.navigation.navigate('MainPage') 
                    this.props.setIsLoggedInProp(true);
                    //this.props.setIsLoggedIn(true);
                    //this.props.setUser(user);
                    //this.props.setToken(response.data);
                    // <Text style={styles.forgot}>{this.props.test}</Text>

                }
            })
            .catch((error) => 
            {

                this.setState({wrongPw: true});
                console.log(error);
            }
            );
        }


    render() {
        return (
            <View style={styles.container}>
                <TouchableOpacity onPress={this._onPressButtonTest.bind(this)} style={styles.loginBtn}>
                    <Text style={styles.loginText}>TESTING</Text>
                </TouchableOpacity>
                <Image source={Logo} style={styles.logo}/>
                <TextInput style={styles.inputView} placeholder='Email' placeholderTextColor="#1F676B"  onChangeText={(value) =>  this.setState({email: value})} />
                <TextInput style={styles.inputView} secureTextEntry={true} placeholder='Password' placeholderTextColor="#1F676B" onChangeText={(value) => this.setState({password: value})} />
                {this.state.wrongPw && <Text style={styles.error}>Email oder Passwort ist ungültig.</Text>}
                <TouchableOpacity  onPress={() => this.props.navigation.navigate('PasswortVergessen')}>
                    <Text style={styles.forgot}>Passwort vergessen?</Text>
                </TouchableOpacity>
                <TouchableOpacity onPress={this._onPressButton.bind(this)} style={styles.loginBtn}>
                    <Text style={styles.loginText}>LOGIN</Text>
                </TouchableOpacity>
                <TouchableOpacity onPress={this._onRegisterButton.bind(this)}>
                    <Text style={styles.register}>Registrieren</Text>
                </TouchableOpacity>
            </View>
            )
    }
}
//null wenn ich nichts will von states
export default connect(mapStateToProps, mapDispatchToProps)(Login);

const styles = StyleSheet.create({
    container: {
      flex: 1,
      backgroundColor: '#1F676B',
      alignItems: 'center',
      justifyContent: 'center',
    },

    logo:{
        width: 280,
        height: 140,
        marginBottom:30,
      },

    inputView:{
        width:275,
        backgroundColor:"#a5c2c3",
        borderRadius:25,
        height:50,
        marginBottom:20,
        justifyContent:"center",
        padding:10,
        color:"#1F676B",
        fontSize:15,
        fontWeight: "bold"
    },
    inputText:{
        height:50,
        color:"#1F676B"
    },
    forgot:{
        color:"white",
        fontSize:12,
        marginBottom:10,
    },
    register:{
        color:"white",
        fontSize:16,
    },
    loginText:{
        color:"#1F676B",
        fontSize:19,
        fontWeight: "bold"
    },
    loginBtn:{
        width:275,
        backgroundColor:"#ffffff",
        borderRadius:25,
        height:50,
        alignItems:"center",
        justifyContent:"center",
        marginTop:40,
        marginBottom:10
      },
    error:{
        color:"#fff",
        fontSize:19,
        marginBottom:20
    },

  })

I am searching for the error the last few hours and can't seem to make progress. Any tips are appreciated.

EDIT: So the error was in the actions. My actions were wrong defined. This is how it worked for me:

export function setIsLoggedIn(value)
{
    const action =
    {
        type: SET_IS_LOGGED_IN,
        value
    };
    return action; 
}
2

2 Answers

0
votes

Try constructing the actions following the convention { type: SET_IS_LOGGED_IN, payload: value }. I think {value} tries to destructure value instead of setting a new prop value: true in your action object. Let me know if it works.

0
votes

for async operations you should not execute dispath and dispatch the action, rather pass that as the second argument to the setIsLogedIn function


export function setIsLoggedInAsync(value)
{
    return dispatch =>
    {
        setTimeout(() => 
        {
            dispatch(setIsLoggedIn(value))
        },1000);
    };
}

you need to provide is this way in the mapDispatchToProps function,

 setIsLoggedIn: value => setIsLoggedIn(true)(dispatch), // do this
 // setIsLoggedIn: value => dispatch(setIsLoggedIn(true)) dont

in your approach, you are executing the dispatch, and setIsLoggedIn dosent return an action object, (object with action key) and setIsLoggedIn dosnt return a action object, rather it returns a thunk, a function;.