2016-12-09 23 views
1

フォームデータを保存しようとしたときにこのエラーが断続的に発生しました。 UserFormonSaveは、以下のコンポーネントコードのsaveUserメソッドに戻ります。ReactとRedux:キャッチされていないエラー:ディスパッチ間で状態の変化が検出されました

私はこの上Reduxのドキュメントを見てきたし、非常に私は、具体的にタイトル、中にエラーを引き起こす状態を修正するよどこのまわりで私の頭を取得することはできません。Uncaught Error: A state mutation was detected between dispatches, in the path 'users.2'. This may cause incorrect behavior.

限り私は、ローカルの変更だけが行われていることを伝えることができ、減速機は、自分の変更が加えられたグローバル状態のコピーを返しています。私は何かを欠いているに違いないが、何?ここで

は、コンポーネントのコードです:

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 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; 

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層です

+0

を、なぜあなたは、ユーザーとユーザーに戻ってきていますか? – Kafo

+0

@HusseinAlkafあなたは正しいです、それは必要ではありません。私はこのコンポーネントを別のコンポーネントから再利用しました。もちろん、エラーには影響しませんが、編集します。ありがとう! –

答えて

8

@DDSは、問題を引き起こしていたのは他の場所の突然変異であるという点で、正しい方向に感謝しました(感謝!)。

ManageUserPageはDOMの最上位コンポーネントですが、UsersPageと呼ばれる別のルートの別のコンポーネントは、そのレンダリングメソッドで状態が変更されています。

は当初renderメソッドはこのように見えた:

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割り当てを変更し、問題が解決されました:mapStateToPropsで

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

このコンポーネントまたはレデューサーに問題はありません。おそらくusers[ix] = savedUserをどこかに割り当てている親コンポーネントにあり、ユーザー配列は最終的に状態の配列と同じ配列です。

関連する問題