React-redux: React native props not updated after calling an action

Created on 16 Jul 2017  路  3Comments  路  Source: reduxjs/react-redux

I'm new to react, react native, and redux, I have a react native app that has a login function and when pressed, it will login the user through props.login(...), and then update the props.
Here my code.

render() {
       return (<TouchableOpacity style={Style.btnSubmit} onPress={this.onLoginPressed.bind(this)}>
                <Text style={Style.btnText}>Login</Text>
              </TouchableOpacity>)
}
onLoginPressed() {
        const { username, password } = this.props.form;
        this.props.login({ username, password}); // the login is using the fetch api

        // props are not updated with the new state values
        console.log(this.props)
    }
function mapStateToProps(state) {
    return {
        ...state.login
    }
}

function mapDispatchToProps(dispatch) {
    return {
        login: (formData) => dispatch(login(formData)),
        facebookLogin: (formData) => dispatch(facebookLogin(formData)),
        setUsername: (username) => dispatch(setUsername(username)),
        setPassword: (password) => dispatch(setPassword(password)),        
    }
}


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

here is my action

import { Host, Endpoints } from '../config/server';
import { loginActions } from '../config/constants';


/*
    * state props
        - form
        - inProgress
        - error
        - data
*/

export function login(form) {
    return (dispatch) => {
        dispatch(loggingIn(true));
        fetch(Host + Endpoints.auth.login, {
            method: 'POST',
            headers: {
                'content-type': 'application/json'
            },
            body: JSON.stringify(form)
        })
            .then(res => res.json())
            .then(res => { 
                dispatch(loggingIn(false));                
                res.error ? dispatch(loginError(res.error)) : 
                            dispatch(loginSuccess(res.data));
            })
            .catch(err => dispatch(loginError(err)));
    }
}

export function facebookLogin(data) {
    return (dispatch) => {
        dispatch(loggingIn());

        fetch(Host + Endpoints.auth.facebookLogin, {
            method: 'POST',
            headers: {
                'content-type': 'application/json'
            },
            body: JSON.stringify(data)
        })
        .then(res => res.json())
        .then(data => dispatch(loginSuccess(data)))
        .catch(err => dispatch(loginError(err)));
    }
}

export function setUsername(username) {
    return {
        type: loginActions.setUsername,
        username
    }
}

export function setPassword(password) {
    return {
        type: loginActions.setPassword,
        password
    }
}

function loginSuccess(data) {
    return {
        type: loginActions.LoginSuccess,
        data
    }
}

function loginError(error) {
    return {
        type: loginActions.LoginError,
        error
    }
}

function loggingIn(val) {
    return {
        type: loginActions.LoggingIn,
        inProgress: val
    }
}

here is the reducer

import { loginActions } from '../config/constants';

const initialState = {
  form: {
    username: '',
    password: ''
  },
  data: null,
  inProgress: false,
  error: null
};

export default function loginReducer(state = initialState, action) {
    switch(action.type) {
        case loginActions.LoggingIn: 
            return {
                ...state,
                inProgress: action.inProgress
            }            
        case loginActions.LoginError:
             return {
                ...state, 
                error: action.error,
            }

        case loginActions.LoginSuccess:
            return {
                ...state,
                inProgress: false,
                error: null,
                data: action.data
            }
        case loginActions.setUsername:
            return {
                ...state,
                form: { 
                    username: action.username,
                    password: state.form.password
                }
            }
        case loginActions.setPassword:
            return {
                ...state,
                form: { 
                    username: state.form.username,
                    password: action.password
                }
            }
        default: 
            return {
                ...state
            }
    }
}

here is the reducer index file

import { combineReducers } from 'redux';
import login from './login';

const rootReducer = combineReducers({
    login
});

export default rootReducer;

here is the configure store

import { createStore, applyMiddleware } from 'redux'
import reducers from './reducers'
import thunk from 'redux-thunk'

export default function configureStore() {
  let store = createStore(reducers, applyMiddleware(thunk))
  return store
}

Of course I'm wrapping my root component with the Provider and passing the store to it.

Am I missing something?
Thank you

Most helpful comment

This is a bug tracker, not a support system. For usage questions, please use Stack Overflow or Reactiflux. Thanks!

All 3 comments

This is a bug tracker, not a support system. For usage questions, please use Stack Overflow or Reactiflux. Thanks!

As Tim said, this should be asked on Stack Overflow. However, I can see the issue.

Props are not updated immediately after an action is dispatched. React renders are generally asynchronous, so if you check a prop's value right after dispatching a Redux action, it will still have the existing value.

Your code looks like it should work otherwise, although I do have a couple stylistic suggestions.

First, connect supports an object shorthand syntax for the mapDispatch argument. You can simplify your code considerably by doing:

const actions = {
    login,
    facebookLogin,
    setUsername,
    setPassword
};

export default connect(mapState, actions)(Login);

Also, you're making unnecessary copies. You definitely don't want to do return {...state} in the reducer default case - you should do return state to return the existing object. Also, you don't need to do return {...state.login} in the mapState function. That probably won't break anything, but you might as well do return state.login just to avoid the unnecessary copy.

@timdorr sorry for posting the question here.

@markerikson, I'm new to react, react-native, and redux, that was really helpful, thank you so much.

Was this page helpful?
0 / 5 - 0 ratings