在组件中处理本地状态和Redux状态

时间:2018-12-05 16:30:02

标签: reactjs redux

当用户尝试登录时,应更新本地状态或Redux状态。 this.props.checkLogin()是指Redux存储。

  1. 如果登录成功,则应该更新Redux存储。 因为整个应用程序都需要此信息。
  2. 如果未成功,则应使用错误更新本地状态 消息(一个布尔值,称为“ showError”)。

在下面的设置中,结果总是在Redux存储中更新(这不是我想要的,但是我看不到任何其他解决方案)。主要问题是,如果操作中有错误,则无法在组件的checkLogin()中捕获错误。

// 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
        })  
    })
}

但是,如果(代码1)中有错误,则Redux状态将更新(使用AUTH_ERR操作)。我想摆脱此操作,因为我只希望它在本地状态下。因为错误登录尝试的布尔值不应存储在Redux中。

我能想到的唯一解决方案如下所示(代码2)。 POST请求已移至组件本身(在我看来,这不是很好)。

// 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)))
}

我的主要问题是:解决此问题的最干净的方法是什么?我可以遵循某种指南吗?该代码(代码2)有效,但是它绝对缺乏设计原则。 解决此问题的另一种方法可能是这样的:使用第一个代码,并在POST请求的捕获中引发错误。被组件捕获的是这样的:

// 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
    })
}

在上面的代码(代码3)中,我看不到如何解决此问题。是否有可能在Redux操作中引发错误,然后由Login组件的checkLogin()捕获该错误?还是我应该另辟path径解决这个问题?

1 个答案:

答案 0 :(得分:1)

您已连接redux来管理应用程序的状态,为什么要避免在redux中存储错误? react-redux(带有redux-thunk中间件)身份验证过程的典型实现如下。

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)