2

When a user tries to login, the Local state or Redux state should be updated. this.props.checkLogin() refers to the Redux store.

  1. If the login is succesful, the Redux store should be updated. Because this information is needed throughout the whole application.
  2. If not succesful, the localstate should be updated with an error message (a boolean which is called 'showError').

In the setup below, the results are always updated in the Redux store (which is not what I want, but I dont see any other solution yet). The main problem is that I can not catch an error in the checkLogin() of the component, if there is an error in the action.

// LOGIN COMPONENT (Code 1)
checkLogin(e){
     e.preventDefault()
     var username = e.target.elements.username.value
     var password = e.target.elements.password.value
     this.props.checkLogin(username, password)
}

// Redux action
export const checkLogin = (username, password) => async dispatch => {
    axios.post('/api/login', {
        username: username,
        password: password
    })
    .then(res => {
        const token = res.data.token;
        localStorage.setItem('jwtToken', token);
        setAuthorizationToken(token);
        dispatch(setCurrentUser(jwt.decode(token)))
    })
    .catch(err => {
        dispatch({
            type: AUTH_ERROR,
            payload: true
        })  
    })
}

However, if there is an error in (Code 1), the Redux state is updated (with the AUTH_ERR action). I want to get rid of this action, as I only want it in local state. Because the boolean of an error login attempt should not be stored in Redux.

The only solution which I could think of is shown below (Code 2). The POST-request has moved to the component itself (which is not nice in my opinion).

// LOGIN COMPONENT (Code 2)
checkLogin(e){
    e.preventDefault()
    var username = e.target.elements.username.value
    var password = e.target.elements.password.value
    axios.post('/api/login', {
        username: username,
        password: password
    })
    .then(res => {
        this.props.checkLogin(username, password)
    }
    .catch(err => {
        this.setState({
            showError: true
        }) 
    })
}

// Redux action
export const checkLogin = (username, password) => async dispatch => {
    const token = res.data.token;
    localStorage.setItem('jwtToken', token);
    setAuthorizationToken(token);
    dispatch(setCurrentUser(jwt.decode(token)))
}

My main question is: what is the cleanest way of solving this? Is there some sort of guide which I can follow? The code (Code 2) works, but it definitely lacks design principles. Another way of solving this could be something like this: use the first code and throw an error in the catch of the POST-request. Which is catched by the component, like this:

// LOGIN COMPONENT (Code 3)
checkLogin(e){
    e.preventDefault()
    var username = e.target.elements.username.value
    var password = e.target.elements.password.value
    this.props.checkLogin(username, password).catch(res => {
        this.setState({
            showError: true
        }) 
    })
}

// Redux action
export const checkLogin = (username, password) => async dispatch => {
    axios.post('/api/login', {
        username: username,
        password: password
    })
    .then(res => {
        const token = res.data.token;
        localStorage.setItem('jwtToken', token);
        setAuthorizationToken(token);
        dispatch(setCurrentUser(jwt.decode(token)))
    })
    .catch(err => {
        // Return an error here which is cacthed in the component
    })
}

In the above code (Code 3), I can't see how to solve this. Is it possible to throw an error in the Redux action, which is then catched by the checkLogin() of the Login-component? Or should I complete take another path in solving this?

2
  • Why do you want to store the data in local state instead of in redux? In my experience, mixing local state and redux state is confusing and can cause synchronisation issues that are hard to debug. Whenever I think I have a use case for local state and redux, I always end up with reasons to just stick with redux Commented Dec 5, 2018 at 17:23
  • @ChristopherMoore The error message should be removed (so 'showError: false') when the Login-component is unmounted. For example: when I go the the Forgot-page and then go back to the login, the error message is still shown (what I don't want). A possibility is to do something with componentWillUnmount(), but this adds complexity to the application. In my opinion, it is unnecessary to store this information in Redux, because it is only used on the Login-page. Commented Dec 5, 2018 at 20:51

1 Answer 1

1

You have connected redux to manage the state of the application, why do you want to avoid storing an error in redux? A typical implementation of the react-redux (with redux-thunk middleware) authentication process is as follows.

actions.js

export const auth = (username, password) => {
  dispatch(authStart())
  axios.post('/api/login', {
    username: username,
    paswword: password
  }).then(request => {
    dispatch(authSuccess(response.authToken, response.userId))
  }).catch(error => {
    dispatch(authFail(error))
  })
})

const authStart = () => {
  return { type: 'AUTH_START'}
}

const authSuccess = (authToken, userId) => {
  return { type: 'AUTH_SUCCESS', authToken: authToken, userId: userId }
}

const authFail = (error) => {
  return { type: 'AUTH_FAIL', error: error }
}

reducer.js

const initialState = {
  userId: null,
  authToken: null,
  error: null
  loading: false
}

const authStart = (state, action) => {
  return { ...state, loading: true, error: null }
}

const authSuccess = (state, action) => {
  return { 
    ...state,
    loading: false,
    authToken: action.authToken,
    userId: actions.userId
  }
}

const authStart = (state, action) => {
  return { ...state, loading: false, error: error }
}    


const authReducer = (state = initialState, action) => {
  switch action.type:
    case 'AUTH_START':
      return authStart(state, action)
    case 'AUTH_SUCCESS':
      return authSuccess(state, action)
    case 'AUTH_FAIL':
      return authFail(state, action)
    default:
      return state
}

component.js

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { auth } from './actions.js'

class MyComponent extends Component {
  checkLogin(e){
    e.preventDefault()
    var username = e.target.elements.username.value
    var password = e.target.elements.password.value
    this.props.signIn(username, password)
  }

  render() {
    let content = <div>Login succeed. User id is {this.props.userId}</div>
    if (this.props.loading) {
      content = <div>Loading...</div>
    }
    if (this.props.error) {
      content = <div>Error: {this.props.error}</div>
    }
    return <React.Fragment>{content}</React.Fragment>
  }

}

const mapStateToProps = state => {
  return {
    error: state.auth.error,
    loading: state.auth.loading,
    userId: state.auth.userId
  }
}

const mapDispatchToProps = dispatch => {
  return {
    signIn: (username, password) => dispatch(auth(username, password))
  }
}


export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)
Sign up to request clarification or add additional context in comments.

3 Comments

I don't really understand what will happen if you go to another page and then go back. Will the error still be displayed? This is what my main issue is: the state of the error message is only important on the Login component, and nowhere else in the application
It depends on how you act. You can perform the necessary processing (in the code above, this is displaying a message with an error), or ignore the error. After your actions, you should set the error property to null. So that the error message is no longer displayed.
I think I will use 'componentWillUnmount()' and then create an action type with RESET.

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.