Danny
Danny

Reputation: 107

Return promise from action in redux

i am new in react-redux and for the time been i have a simple page doing login to an existing service(the call is working and i am getting data from the server), i want to get the data in the component and set set cookie and redirect the user based in the data received from the server. I am getting the data in the action but in the component it is undefined. Thank you all

The submit function in the form component:

onSubmit(e) {
        e.preventDefault();
        if (this.isValid()) {
            this.setState({ errors: {}, isLoading: true });
            this.props.actions.loginUser(this.state).then(
                function (res) {
                    this.context.router.push('/');
                },
                function (err) {
                    this.setState({ errors: err.response.data.errors, isLoading: false });
                }
            );
        }
    }

The action function :

import * as types from './actionTypes';
import loginApi from '../api/loginApi';

export function loginSuccess(result) {
    return { type: types.LOGIN_SUCCESS, result };
}

export function loginFailed(result) {
    return { type: types.LOGIN_FAILURE, result };
}

export function loginUser(data) {
    return dispatch => {
        return loginApi.loginUser(data).then(result => {
            if (result) {
                if (result.ErrorCode == 0)
                    dispatch(loginSuccess(result));
                else
                    dispatch(loginFailed(result));
            }
        }).catch(error => {
            throw (error);
        });
    };
}

The api:

static loginUser(data) {
   return fetch(API + "/SystemLogin.aspx",
      {
        method: 'POST',
        mode: 'cors',
        body: JSON.stringify({
          User: data.identifier,
          Password: data.password
        })
      }).then(function (response) {
        return response.json();
      })
      .then(function (parsedData) {
        return parsedData;
        //resolve(parsedData);
      })
      .catch(error => {
        return error;
      });
  }

The reducer:

import * as types from '../actions/actionTypes';
import LoginApi from '../api/loginApi';
import initialState from './initialState';

export default function loginReducer(state = initialState.login, action) {
    switch (action.type) {
        case types.LOGIN_SUCCESS:
        return [...state, Object.assign({}, action.courses)];
            //return action.result;
        case types.LOGIN_FAILURE:
            return action.result;
        default:
            return state;
    }
}

Is there any more code needed?

Upvotes: 1

Views: 2326

Answers (3)

VivekN
VivekN

Reputation: 1602

First issue is with your reducer code. It should be like this:-

import * as types from '../actions/actionTypes';
import LoginApi from '../api/loginApi';
import initialState from './initialState';

export default function loginReducer(state = initialState.login, action) {
    switch (action.type) {
        case types.LOGIN_SUCCESS:
        return Object.assign({}, state, { userProfile: action.results });
            //return action.result;
        case types.LOGIN_FAILURE:
            return action.result;
        default:
            return state;
    }
}

The second problem is with the way you are trying to send the user to another route. It should be like this:-

onSubmit(e) {
        e.preventDefault();
        if (this.isValid()) {
            this.setState({ errors: {}, isLoading: true });
            this.props.actions.loginUser(this.state);
        }
    }

The next problem is inside the render function.Inside render function, put a check for is your this.props.userProfile object is defined or not and if it is which will only happen on login success, then redirect him/her to the /homepage route using

this.context.router.push("/homepage");

Upvotes: 2

minhtran612
minhtran612

Reputation: 65

Could you check that did you connect the form component with redux ? If yes, you should access the action directly via this.props instead of this.props.actions.loginUser. Maybe this is not the problem because I haven't had a chance to look for the full code of your form component.

Hope this helps.

Upvotes: 0

Bamieh
Bamieh

Reputation: 10906

Although the code might be working, it is certainly a hassle to do all the above, the code is not easily comprehensible this way, since you are mixing "dirty" async code within the component and actions.

I advise you to use redux-saga to separate the side-effects from the component and actions.

A good starting point here.

step 1: fire an action

step 2: catch the action inside a "saga" instead of a reducer:

import { put, takeEvery, all } from 'redux-saga/effects'

// Our worker Saga: will perform the async increment task
export function* loginUserAsync() {
  yield put({ type: 'LOGIN_SUCCESS' })
}

// Our watcher Saga: spawn a new loginUserAsync task on each LOGIN_USER
export function* watchIncrementAsync() {
  yield takeEvery('LOGIN_USER', loginUserAsync)
}

step 3: fire a new action after the async code is done and catch it inside a reducer to update state.

step 4: if you have more "dirty" code, simply run it inside the saga (redirection for example)

Upvotes: 1

Related Questions