2016-12-09 20 views
1

我正在使用「受控」組件(在組件中使用setState()),並在嘗試保存表單數據時間歇性地獲取此錯誤。 UserFormonSave回調到下面組件代碼中的saveUser方法。React和Redux:Uncaught錯誤:在調度之間檢測到狀態突變

我看了看這個終極版文檔並不能完全得到我的周圍,我正在修改狀態,使的稱號,這是專門的錯誤頭:Uncaught Error: A state mutation was detected between dispatches, in the path 'users.2'. This may cause incorrect behavior.

至於我可以說,只有本地修改正在進行,並且reducer正在返回全局狀態的副本並添加了我的更改。我一定錯過了什麼,但是什麼?

這裏的分量代碼:

import React, {PropTypes} from 'react'; 
import {connect} from 'react-redux'; 
import {bindActionCreators} from 'redux'; 
import * as userActions from '../../actions/userActions'; 
import UserForm from './UserForm'; 


export class ManageUserPage extends React.Component { 
    constructor(props, context) { 
    super(props, context); 

    this.state = { 
     user: Object.assign({}, this.props.user), 
     errors: {}, 
     saving: false 
    }; 
    this.updateUserState = this.updateUserState.bind(this); 
    this.saveUser = this.saveUser.bind(this); 
    } 

    componentWillReceiveProps(nextProps) { 
    if (this.props.user.id != nextProps.user.id) { 
     this.setState(
     { 
      user: Object.assign({}, nextProps.user) 
     } 
    ); 
    } 
    } 

    updateUserState(event) { 
    const field = event.target.name; 
    let user = Object.assign({}, this.state.user); 
    user[field] = event.target.value; 
    return this.setState({user: user}); 
    } 

    userFormIsValid() { 
    let formIsValid = true; 
    let errors = {}; 

    if (this.state.user.firstName.length < 3) { 
     errors.firstName = 'Name must be at least 3 characters.'; 
     formIsValid = false; 
    } 
    this.setState({errors: errors}); 
    return formIsValid; 
    } 


    saveUser(event) { 
    event.preventDefault(); 
    if (!this.userFormIsValid()) { 
     return; 
    } 
    this.setState({saving: true}); 
    this.props.actions 
     .saveUser(this.state.user) 
     .then(() => this.redirect()) 
     .catch((error) => { 
     this.setState({saving: false}); 
     }); 
    } 

    redirect() { 
    this.setState({saving: false}); 
    this.context.router.push('/users'); 
    } 

    render() { 
    return (
     <UserForm 
     onChange={this.updateUserState} 
     onSave={this.saveUser} 
     errors={this.state.errors} 
     user={this.state.user} 
     saving={this.state.saving}/> 
    ); 
    } 
} 

ManageUserPage.propTypes = { 
    user: PropTypes.object.isRequired, 
    actions: PropTypes.object.isRequired 
}; 

ManageUserPage.contextTypes = { 
    router: PropTypes.object 
}; 


function getUserById(users, userId) { 
    const user = users.find(u => u.id === userId); 
    return user || null; 
} 

function mapStateToProps(state, ownProps) { 
    let user = { 
    id:  '', 
    firstName: '', 
    lastName: '' 
    }; 

    const userId = ownProps.params.id; 

    if (state.users.length && userId) { 
    user = getUserById(state.users, userId); 
    } 


    return { 
    user: user 
    }; 
} 

function mapDispatchToProps(dispatch) { 
    return { 
    actions: bindActionCreators(userActions, dispatch) 
    }; 
} 

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

這裏的減速機:

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


export default(state = initialState.users, action) => 
{ 
    switch (action.type) { 
    case types.CREATE_USER_SUCCESS: 
     return [ 
     // grab our state, then add our new user in 
     ...state, 
     Object.assign({}, action.user) 
     ]; 

    case types.UPDATE_USER_SUCCESS: 
     return [ 
     // filter out THIS user from our copy of the state, then add our updated user in 
     ...state.filter(user => user.id !== action.user.id), 
     Object.assign({}, action.user) 
     ]; 

    default: 
     return state; 
    } 
}; 

這裏是操作:

import * as types from './actionTypes'; 
import userApi from '../api/mockUserApi'; 
import {beginAjaxCall, ajaxCallError} from './ajaxStatusActions'; 


export function createUserSuccess(user) { 
    return {type: types.CREATE_USER_SUCCESS, user}; 
} 

export function updateUserSuccess(user) { 
    return {type: types.UPDATE_USER_SUCCESS, user}; 
} 

export function saveUser(user) { 
    return function (dispatch, getState) { 
    dispatch(beginAjaxCall()); 
    return userApi.saveUser(user) 
     .then(savedUser => { 
     user.id 
      ? dispatch(updateUserSuccess(savedUser)) 
      : dispatch(createUserSuccess(savedUser)); 
     }).catch(error => { 
     dispatch(ajaxCallError(error)); 
     throw(error); 
     }); 
    }; 
} 

這裏的模擬API層:

import delay from './delay'; 

const users = [ 
    { 
    id: 'john-smith', 
    firstName: 'John', 
    lastName: 'Smith' 
    } 
]; 

const generateId = (user) => { 
    return user.firstName.toLowerCase() + '-' + user.lastName.toLowerCase(); 
}; 

class UserApi { 
    static saveUser(user) { 
    user = Object.assign({}, user); 
    return new Promise((resolve, reject) => { 
     setTimeout(() => { 
     const minUserNameLength = 3; 
     if (user.firstName.length < minUserNameLength) { 
      reject(`First Name must be at least ${minUserNameLength} characters.`); 
     } 

     if (user.lastName.length < minUserNameLength) { 
      reject(`Last Name must be at least ${minUserNameLength} characters.`); 
     } 

     if (user.id) { 
      const existingUserIndex = users.findIndex(u => u.id == u.id); 
      users.splice(existingUserIndex, 1, user); 
     } else { 
      user.id = generateId(user); 
      users.push(user); 
     } 
     resolve(user); 
     }, delay); 
    }); 
    } 
} 

export default UserApi; 
+0

,爲什麼要退用戶和用戶? – Kafo

+0

@HusseinAlkaf你是對的,這不是必需的。我從另一個組件重用了這個組件。當然,它不會影響錯誤,但會進行編輯。謝謝! –

回答

8

@DDS指出我在正確的方向(謝謝!),因爲它是導致問題的其他地方的突變。

ManageUserPage是DOM中的頂級組件,但另一個名爲UsersPage的路由上的另一個組件在其渲染方法中處於變異狀態。

最初的渲染方法是這樣的:

render() { 
    const users = this.props.users.sort(alphaSort); 
    return (
     <div> 
     <h1>Users</h1> 
     <input type="submit" 
       value="Add User" 
       className="btn btn-primary" 
       onClick={this.redirectToAddUserPage}/> 
     <UserList 
      users={users}/> 
     </div> 
    ); 
} 

我改變了users分配到以下和問題解決:

const users = [...this.props.users].sort(alphaSort); 
在mapStateToProps
4

問題不在於此組件或reducer中。它可能在父組件中,您可能在其中指定users[ix] = savedUser,而用戶數組最終與狀態中的數組相同。

相關問題