2017-04-18 13 views
0

私のReact Appで奇妙な動作が発生しています。店舗に経費を追加すると、削除方法deleteExpenseが自動的に呼び出され、最後に追加したアイテムが削除されます。私のupdateExpenseメソッドからthis.setStateを削除すると、これは起こりません。なぜこれが起こっているのか分かりません。ここでは、コードは次のとおりです。setStateが自動的に別のメソッドを呼び出す

App.jsx:

import React from 'react'; 
import { observer } from 'mobx-react'; 
import DevTools from 'mobx-react-devtools'; 

import ExpenseStore from './stores/ExpenseStore'; 
import AddExpenseView from './components/AddExpenseView'; 
import CategoryView from './components/CategoryView'; 

var ExpenseStoreObj = new ExpenseStore(); 

@observer 
export default class App extends React.Component { 
    constructor() { 
    super(); 
    this.state = { 
     allExpenses: ExpenseStoreObj.expenses, 
     expenseCategories: ExpenseStoreObj.expenseCategories 
    } 
    this.updateExpense = this.updateExpense.bind(this); 
    this.deleteExpense = this.deleteExpense.bind(this); 
    this.categorizeExpenses = this.categorizeExpenses.bind(this); 
    } 

    updateExpense(newExpense) { 
    ExpenseStoreObj.addExpense(newExpense); 
    this.setState({ 
     allExpenses: ExpenseStoreObj.expenses.slice() 
    }) 
    } 

    deleteExpense(ExpenseId) { 
    ExpenseStoreObj.removeExpense(ExpenseId); 
    } 

    categorizeExpenses() { 
    return ExpenseStoreObj.categorizeExpenses(); 
    } 

    render() { 
    return (
     <div className="app-container"> 
     <div className="col-xs-6 border"> 
      <AddExpenseView 
      allExpenses={this.state.allExpenses} 
      expenseCategories={this.state.expenseCategories} 
      updateExpense={this.updateExpense} 
      deleteExpense={this.deleteExpense} 
      /> 
     </div> 
     <div className="col-xs-6 border"> 
      <CategoryView 
      categorizeExpenses={this.categorizeExpenses} 
      /> 
     </div> 
     </div> 
    ); 
    } 
} 

ExpenseStore.js:コールスタックの検査で

import { observable, computed, reaction } from 'mobx'; 

export default class ExpenseStore { 
    constructor(props) { 
     this.id = 1; 
    } 
    expenses = []; 
    expenseCategories = ['Food', 'Clothing', 'Transport']; 

    handleError(message) { 
     alert(message); 
    } 

    addExpense(newExpense) { 
     if (newExpense.expenseName == '') { 
      this.handleError('Please enter expense name'); 
     } 
     else if (newExpense.expenseCategory == '') { 
      this.handleError('Please select expense categorry'); 
     } 
     else if (newExpense.expensePrice == '') { 
      this.handleError('Please enter expense value'); 
     } 
     else { 
      newExpense.id = this.id++; 
      this.expenses.push(newExpense); 
      this.categorizeExpenses(); 
      console.log('expense store: ', this.expenses); 
     } 
    } 

    removeExpense(expenseId) { 
     let expenseIndex = this.expenses.findIndex(x => x.id == expenseId); 
     this.expenses.splice(expenseIndex, 1); 
     console.log('after deleting expense', this.expenses); 
    } 

    categorizeExpenses() { 
     let groupedResult = this.expenses.reduce(function (hash) { 
      return function (r, a) { 
       if (!hash[a.expenseCategory]) { 
        hash[a.expenseCategory] = { expenseCategory: a.expenseCategory, expensePrice: 0 }; 
        r.push(hash[a.expenseCategory]); 
       } 
       console.log('hash[a.expenseCategory].expensePrice', hash[a.expenseCategory].expensePrice); 
       console.log('a.expensePrice', a.expensePrice); 
       hash[a.expenseCategory].expensePrice = parseInt(hash[a.expenseCategory].expensePrice) + parseInt(a.expensePrice); 
       return r; 
      }; 
     }(Object.create(null)), []); 
     return groupedResult; 
    } 

} 

、私が反応は何とかListExpenses.jsで定義されたhandleExpenseDeleteを呼び出していることが判明しました(これはApp.jsxの 'removeExpense'機能を小道具として取得します。

ListExpenses.js

あなたのrenderメソッドで
import React from 'react'; 
import PropTypes from 'prop-types'; 
import { observer } from 'mobx-react'; 

//@observer 
export default class ListExpenses extends React.Component { 
    constructor(props) { 
     super(props); 
     this.handleExpenseDelete = this.handleExpenseDelete.bind(this); 
    } 

    handleExpenseDelete(index) { 
     this.props.deleteExpense(index); 
    } 

    render() { 
     var listItems = this.props.allExpenses.map((obj, index) => { 
      return (
       <tr key={index}> 
        <td>{obj.expenseName}</td> 
        <td>{obj.expenseCategory}</td> 
        <td>{obj.expensePrice}</td> 
        <td><button onClick={this.handleExpenseDelete(index)}>Delete</button></td> 
       </tr> 

      ) 
     }); 
     return (
      <div> 
       <table className="table table-striped table-responsive"> 
        <thead> 
         <tr> 
          <th>Item</th> 
          <th>Category</th> 
          <th>Price</th> 
          <th></th> 
         </tr> 
        </thead> 
        <tbody> 
         { 
          listItems 
         } 
        </tbody> 
       </table> 
      </div> 
     ); 
    } 
} 

答えて

4

は、レンダリング中

<td><button onClick={() => this.handleExpenseDelete(index)}>Delete</button></td> 

の下にあなたがメソッドを呼び出しているようなものにbuttononClickを変更します。上記のコードでは、onClickだけを呼び出します。

EDIT:以下のコードが所定の位置にあるとき

は、方法(handleExpenseDelete)は、レンダリング中に実行されます。 {}中括弧はその内部の式を実行する必要があることを示しているため、内部の式は関数を呼び出すことです。

onClick={this.handleExpenseDelete(index)} 

このようにすると{}内の式が実行され、関数が生成され、ボタンをクリックするとこの関数が呼び出されます。

onClick={() => this.handleExpenseDelete(index)} 
+0

パンサーの答えが正しいです。 –

+0

が動作しますが、このコードが元のコードとはどのように異なるのか説明できますか?あなたは「レンダリング中にメソッドを呼び出す」と言っています。どのように 'handleExpenseDelete'がレンダリング時にonClickに定義されているときに呼び出されていますか? – maverick

+1

コンストラクタからバインドを削除して、この場合もonClick = {this.handleExspenseDelete.bind(this、index)}とすることもできます。レンダリングでes6 arrow関数を使用する必要がある場合は、コンストラクタでバインドすることによる利点はありません。同じ猫、別の肌。 –

2

理由onClickfunctionを期待しているあなたはonClick={this.handleExpenseDelete(index)}によってそのfunction、その関数呼び出し、handleExpenseDeleteは各renderingではなく、特定のeventに呼び出されますを呼び出して値を返すされています。

あなたはこのようにそれを記述する必要があります。

onClick={() => this.handleExpenseDelete(index)} 

または.bind()を使用すると、このように、binding from constructorを削除します。

onClick={this.handleExpenseDelete.bind(this, index)} 
関連する問題