Redux导入副作用返回Reducer返回未定义

时间:2017-12-23 15:57:23

标签: reactjs redux react-redux

我有以下redux模块:

import { Map, fromJS } from 'immutable'
import api from '../lib/api'
import config from '../config/application'
import storage from '../lib/storage'
import { history } from '../lib/create-store'

// Actions
const USER_LOGIN = 'ts/user/USER_LOGIN'
const USER_LOGIN_RESPONSE = 'ts/user/USER_LOGIN_RESPONSE'
const USER_LOGOUT = 'ts/user/USER_LOGOUT'
const CREATE_ACCOUNT_TEAM = 'ts/user/CREATE_ACCOUNT_TEAM'
const CREATE_ACCOUNT_TEAM_RESPONSE = 'ts/user/CREATE_ACCOUNT_TEAM_RESPONSE'
const RECEIVE_ACCESS_TOKEN = 'ts/user/RECEIVE_ACCESS_TOKEN'
const RECEIVE_USER = 'adsdean/user/RECEIVE_USER'

const initialState = Map({
  accessToken: null,
  loginPending: false,
  loginError: false,
  creatingAccountTeam: false,
  creatingAccountTeamSuccess: false,
  creatingAccountTeamError: Map({}),
  profile: Map({
    id: null,
    email: null,
    firstName: null,
    lastName: null,
    company: null,
    mobile: null,
    mobileShare: true,
    dob: null
  })
})

// Reducer
export default function user (state = initialState, action = {}) {
  switch (action.type) {
    case RECEIVE_ACCESS_TOKEN: {
      storage.save('key', action.token)
      api.setAuth(action.token)
      return state.set('accessToken', fromJS(action.token))
    }

    case USER_LOGIN:
      return state.set('loginPending', true)

    case USER_LOGIN_RESPONSE: {
      let nextState = state.set('loginPending', false)

      if (action.status === 'success') {
        nextState = nextState
          .set('loginError', initialState.loginError)
      } else {
        nextState = nextState.set('loginError', action.error)
      }

      return nextState
    }

    case USER_LOGOUT: {
      storage.delete('key')
      api.setAuth(null)
      return state.set('accessToken', null)
    }

    case CREATE_ACCOUNT_TEAM:
      return state.set('creatingAccountTeam', true)

    case CREATE_ACCOUNT_TEAM_RESPONSE: {
      console.log(action)

      let nextState = state.set('creatingAccountTeam', false)

      if (action.status === 'success')
        state.set('creatingAccountTeamSuccess', true)
      else
        nextState = nextState.set('creatingAccountTeamError', fromJS(action.error))

      return nextState
    }

    case RECEIVE_USER:
      return state
        .setIn('profile', 'id', action.payload.id)
        .setIn('profile', 'email', action.payload.email)
        .setIn('profile', 'firstName', action.payload.first_name)
        .setIn('profile', 'lastName', action.payload.last_name)
        .setIn('profile', 'company', action.payload.company)
        .setIn('profile', 'mobile', action.payload.mobile)
        .setIn('profile', 'mobileShare', action.payload.mobile_share)
        .setIn('profile', 'dob', action.payload.dob)
  }

  return state
}

// ==============================
// Action Creators
// ==============================

export const userLoginResponse = (status, error) => ({
  type: USER_LOGIN_RESPONSE,
  status,
  error,
})

export const receiveAccessToken = token => ({
  type: RECEIVE_ACCESS_TOKEN,
  token
})

export const userCreateWithTeamResponse = (status, error) => ({
  type: CREATE_ACCOUNT_TEAM_RESPONSE,
  status,
  error,
})

export const receiveUser = user => ({
  type: RECEIVE_USER,
  payload: user
})

export const getAccessToken = state =>
  state.get('accessToken')

export const isLoggedIn = state =>
  state.get('accessToken')


// ==============================
// SIDE EFFECTS
// ==============================
//
//

export const getUser = () => async dispatch => {

  api.request.get('/user')
    .then(response => {
      dispatch(receiveUser(response.data))
    })
    .catch(error => {
    })
}

export const userLogin = (username, password) => async dispatch => {
  dispatch({ type: USER_LOGIN })

  api.request.post('/oauth/token', {
    username,
    password,
    'grant_type': 'password',
    'client_id': config.clientId,
    'client_secret': config.clientSecret,
  })
  .then(response => {
    console.log(response)
    const { access_token } = response.data
    dispatch(userLoginResponse('success', null))
    dispatch(receiveAccessToken(access_token))
    dispatch(receiveUser(response.data))
    window.gtag('event', 'login')
  })
  .catch(error => {
    console.log(error)
    dispatch(userLoginResponse('error', error))
  })
}

export const userLogout = () => dispatch => {
  dispatch({ type: USER_LOGOUT })
  history.push('/')
  window.gtag('event', 'logout')
  //logFormSubmission('Logout')
}

export const userCreateWithTeam = (form) => async dispatch => {
  dispatch({ type: CREATE_ACCOUNT_TEAM })

  api.request.post('/account/register/team', {
    email: form.email,
    password: form.password,
    'first_name': form.firstName,
    'last_name': form.lastName,
    company: form.company,
    name: form.name,
    location: form.location,
    dob: form.dob,
    mobile: form.mobile,
    'mobile_share': (form.mobileShare === true) ? 1 : 0
  })
  .then(response => {
    console.log(response)
    dispatch(userCreateWithTeamResponse('success', null))
    dispatch(userLogin(form.email, form.password))
    window.gtag('event', 'create account and team')
    window.gtag('set', {'user_id': response.data.id})
  })
  .catch(error => {
    console.log(error.response)
    dispatch(userCreateWithTeamResponse('error', error.response.data))
  })
}

一切正常,直到我添加名为getUser的副作用函数。一旦我从我的一个组件/容器中import { getUser } from '../../modules/user',我就会被

命中

Error: Reducer "user" returned undefined during initialization. If the state passed to the reducer is undefined, you must explicitly return the initial state. The initial state may not be undefined.

一旦我注释掉导入,使用该模块的其他所有内容都会继续正常工作。那个新的副作用是什么,看起来和造成这个的其他副作用相同?

我的创建商店代码:

import { createStore, applyMiddleware, compose } from 'redux'
import { ConnectedRouter, routerReducer, routerMiddleware, push } from 'react-router-redux'
import createHistory from 'history/createBrowserHistory'
import { combineReducers } from 'redux-immutablejs'
import { fromJS, Map } from 'immutable'
import thunk from 'redux-thunk'
import user from '../modules/user'

const initialState = fromJS({})
const enhancers = []

const middleware = [
  thunk,
  routerMiddleware(history)
]

export const history = createHistory()

const reducer = combineReducers({
  user,
  router: routerReducer,
})

if (process.env.NODE_ENV === 'development') {
  const devToolsExtension = window.devToolsExtension

  if (typeof devToolsExtension === 'function') {
    enhancers.push(devToolsExtension())
  }
}

const composedEnhancers = compose(
  applyMiddleware(...middleware),
  ...enhancers
)

const store = createStore(
  reducer,
  initialState,
  composedEnhancers
)

export default store

我可以通过将initialState Map直接移动到reducer的默认参数部分来修复错误......似乎在将它放在单独的const中时会产生错误。然而,在推出这个功能之前,它已经在这周工作了几个星期......

e.g

// Reducer
export default function user (state = Map({
  accessToken: null,
  loginPending: false,
  loginError: false,
  creatingAccountTeam: false,
  creatingAccountTeamSuccess: false,
  creatingAccountTeamError: Map({}),
  profile: Map({
    id: null,
    email: null,
    firstName: null,
    lastName: null,
    company: null,
    mobile: null,
    mobileShare: true,
    dob: null
  })
}), action = {}) {

1 个答案:

答案 0 :(得分:1)

您使用的

setIn语法不正确,嵌套路径需要位于[]内。

    .setIn(['profile', 'id'], action.payload.id)
    .setIn(['profile', 'email'], action.payload.email)
    .setIn(['profile', 'firstName'], action.payload.first_name)
    .setIn(['profile', 'lastName'], action.payload.last_name)
    .setIn(['profile', 'company'], action.payload.company)
    .setIn(['profile', 'mobile'], action.payload.mobile)
    .setIn(['profile', 'mobileShare'], action.payload.mobile_share)
    .setIn(['profile', 'dob'], action.payload.dob)

选中 documentation

也在

if (action.status === 'success')
    state.set('creatingAccountTeamSuccess', true) .  // <--

您错过了将此分配给nextState,这可能会导致不一致