1

I'm working on a login form in a project with React, Redux and Redux-Thunk. Using Redux-Thunk, I'm able to dispatch async actions like delivering the submitted login form to the back-end and bringing back validated data back to the state via reducers. Once the component gets the data it needs, it can then redirect to the page it needs without a problem.

The issue is, right before redirecting the user I need to write some data which came from asynchronous network request to the localStorage. If I don't do this async, the user gets redirected with the initial state values written to the local storage.

As a solution, I'm using promises and timeouts right in the React component to wait for the incoming data.

This approach seems to work but it doesn't feel right, can someone suggest me a better practice?

Here's the code in the component, I filtered most of the irrelevant things to make it as short as possible here.

import React, {Component} from 'react';
import {bindActionCreators} from 'redux';
import {browserHistory} from 'react-router';
import {reduxForm} from 'redux-form';
import {connect} from 'react-redux';

import {validate} from './../utils/login/surfaceValidation';
import {inputAlerts} from './../utils/login/inputAlerts';

import {submitLogin} from './../redux/actions/index';

class Form extends Component {

    componentWillReceiveProps(nextProps) {
        if(nextProps.loginApproved) {
            this.handleValidLogin();
        }
    }

    handleLogin(props) {
        this.props.submitLogin(props);
        // submitLogin is async action handled by redux-thunk it returns
        // this.props.loginApproved and if it's true componentWillReceiveProps
        // will trigger.
    }

    handleValidLogin() {
        this.writeToStorage()
        .then(() => {
            browserHistory.push('/main');
        }).catch((err) => {
            console.log(err);
        });
    }

    writeToStorage(){
        return new Promise((resolve, reject) => {
            setTimeout(() =>{
                localStorage.setItem('user',
                    JSON.stringify({
                        authenticated: true,
                        username: this.props.username,
                        id: this.props.id,
                        token: this.props.token
                    }));
            }, 3000);
            setTimeout(() => {
                if(this.props.token != null) {
                    resolve();
                    console.log('got a token - resolving.');
                } else {
                    reject();
                    console.log('no token - rejecting. ');
                }
            }, 4000);
        });
    }

    render() {

        return (
            // Login form component is here.
            // When user submits form, this.handleLogin() is called. 
        );
    }
}


function mapDispatchToProps(dispatch){
    return bindActionCreators({submitLogin});
}

function mapStateToProps(state) {
    return {
        loginApproved: state.login.loginApproved,
        username: state.login.username,
        id: state.login.id,
        token: state.login.token
    };
}

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

1 Answer 1

1

As far as I know localStorage.seItem is synchronous so you can call function saving data to storage before redirecting.

Sign up to request clarification or add additional context in comments.

4 Comments

The problem is, the values which are going to be written are defined as null in the initial state. If I don't use promises, or even timeouts for that matter, the initial state values get written before the redirect. (Adding this to the question.)
Sounds like you are calling handleValidLogin prematurely. Just don't call handleValidLogin until all the props you need have arrived. e.g. if (nextProps.token && nextProps.username && nextProps.id) { this.handleValidLogin(); }
This approach solves the issue but in a way I hadn't expected. I'm calling the handleValidLogin with nextProps instead of using the this.props argument and the problems goes away. I just don't understand how nextProps is ahead of time compared to the props which I map with react-redux.
componentWillReceiveProps lifecycle hook is invoked as soon as the props are updated but before another render is called. It means that is not ahead of time but just before component is rendered with new props - your component already have new props at this moment but render is to be invoked

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.