4

I'm building an app using React Native and Redux and have come across a problem during implementation. I'm receiving an error that says

setState cannot update during an existing state transition (such as within render or another component's constructor).

When I load the app simulator, it seems to trigger handleLogin() right away (and on a loop) before any user input.

Here's the AuthenticationContainer and the AuthenticationPage code:

AuthenticationContainer.js:

'use strict'

import React, { Component, PropTypes } from 'react';
import AuthenticatePage from '../../components/Authenticate/AuthenticatePage'
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux'
import * as sessionActionCreators from '../../redux/session';
import {Actions, ActionConst} from 'react-native-router-flux'

class AuthenticateContainer extends Component {
    constructor(props) {
        super(props);
        this.state = {
            email: '',
            password: ''
        };
    }

    handleLogin() {
        this.props.doLogin(this.state.email, this.state.password)
            .then(() => Actions.tabbar())
    }

    render() {
        return (
            <AuthenticatePage
                doLogin = {this.handleLogin.bind(this)}
                error = {this.props.error}/>
        );
    }
}

AuthenticateContainer.propTypes = {
    doLogin: PropTypes.func.isRequired,
    email: PropTypes.string,
    password: PropTypes.string,
    error: PropTypes.string.isRequired
};

export default connect(
    (state) => ({isFetching: state.session.isFetching, error: state.session.error}),
    (dispatch) => bindActionCreators(sessionActionCreators, dispatch)
)(AuthenticateContainer)

AuthenticationPage.js

'use strict'

import React, { Component, PropTypes } from 'react';
import {Text, View, TouchableHighlight, AlertIOS} from 'react-native';
import t from 'tcomb-form-native'
import _ from 'lodash'
import EStyleSheet from 'react-native-extended-stylesheet'
import ViewContainer from '../ViewContainer'

// AuthenticatePage.propTypes = {
//     doLogin: PropTypes.func.isRequired,
//     onUpdateForm: PropTypes.func.isRequired,
//     email: PropTypes.string,
//     password: PropTypes.string,
//     error: PropTypes.string.isRequired
// };

var Form = t.form.Form;

var User = t.struct({email: t.String, password: t.String});

const options = {
    auto: 'placeholders',
    fields: {
        email: {
            autoCapitalize: 'none',
            autoCorrect: false
        },

        password: {
            autoCapitalize: 'none',
            autoCorrect: false,
            password: true,
            secureTextEntry: true
        }
    }
};

export default class AuthenticatePage extends Component {
    constructor(props) {
        super(props);
        this.state = {
            value: {
                email: '',
                password: ''
            }
        };
    }

    render() {
        return (
            <ViewContainer>
                <View>
                    <Form
                        ref="form"
                        type={User}
                        options={options}
                        value={this.state.value}/>
                </View>
                <View>
                    <TouchableHighlight
                        onClick={this.props.doLogin()}
                        underlayColor='#99d9f4'>
                            <Text>{_.capitalize('Login')}</Text>
                    </TouchableHighlight>
                </View>
            </ViewContainer>
        )
    }
}

So the handleLogin() function seems to fire immediately on page load and will run repeatedly while throwing the error. Here's the error that I'm receiving from the simulator.

setState error

Any help would be greatly appreciated!

Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
Kaidao
  • 115
  • 3
  • 16
  • Have to ask, why are you altering the state directly while you're also using Redux in the project? – Maarten Bicknese Aug 29 '16 at 05:23
  • Hi Maarten, do you mean within AuthenticationPage? I was trying to alter the state of that component to handle input from the form. How should this be accomplished? – Kaidao Aug 29 '16 at 05:28
  • using a local state to store the value of a text field is fine. but what I dont understand is why do you have email and password as state in both components. you should just have it in the child component and doLogin takes pass and email as parameters in the function call. – John Ruddell Aug 29 '16 at 05:31
  • Hi John, I was under the assumption that the child components should not be passing props as arguments up to the parent. Is that correct? Here's where I was reading about that: http://stackoverflow.com/questions/22639534/pass-props-to-parent-component-in-react-js – Kaidao Aug 29 '16 at 13:20

1 Answers1

8

I believe the problem is here:

onClick={this.props.doLogin()}

You call a function on render, but you should be passing it as a prop instead. Either try to

onClick={() => this.props.doLogin()}

or

onClick={this.props.doLogin.bind(this)}

to fix.

rishat
  • 8,206
  • 4
  • 44
  • 69