我在React App中遇到了奇怪的行为。当我向商店添加费用时,会自动调用删除方法deleteExpense
,从而删除我添加的最后一项。如果我从this.setState
方法中移除updateExpense
,则不会发生这种情况。无法弄清楚为什么会这样。这是代码:
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;
}
}
在检查调用堆栈时,我发现React以某种方式调用ListExpenses.js中定义的handleExpenseDelete
(它将App.jsx的&#39; removeExpense&#39;函数作为道具。
ListExpenses.js
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>
);
}
}
答案 0 :(得分:4)
在您的渲染方法中,将button
onClick
更改为以下内容
<td><button onClick={() => this.handleExpenseDelete(index)}>Delete</button></td>
您在渲染时调用该方法。上面的代码只会在onClick上调用它。
修改强>
当下面的代码到位时,方法(handleExpenseDelete)在渲染期间执行。由于{}括号表示必须执行其中的表达式,因此内部表达式是调用函数。
onClick={this.handleExpenseDelete(index)}
当你这样做时,执行{}内的表达式会产生一个函数,当你点击按钮时会调用这个函数。
onClick={() => this.handleExpenseDelete(index)}
答案 1 :(得分:2)
原因是,onClick
期待function
并且您通过function
调用onClick={this.handleExpenseDelete(index)}
来返回一个值,其函数调用handleExpenseDelete
将会在每个rendering
上调用,而不是在特定的event
上调用。
你需要这样写:
onClick={() => this.handleExpenseDelete(index)}
或者使用.bind()
并删除binding from constructor
,如下所示:
onClick={this.handleExpenseDelete.bind(this, index)}