我正在开发一个简单的待办事项清单' react app(React.js的新手)。我已将项目添加到列表中,但删除项目会引发一个问题。在我的父反应组件中,我有以下代码:
Displays info
Makes empty list studentsNames
Repeats the following 10 times:
Asks the user for ten names and put the string in a variable
Asks the user for the ten ages and tries to turn a string to an integer* and stores that in a variable
Each loop, the variables are overwritten
Gets the highest value from the age variable which would be the last and only one, saves it to a variable
Display that variable
我的print("your info")
names = []
ages = []
for __ in range(10):
name.append(input("Enter student name: "))
age.append(int(input("Age: ")))
oldest_age = max(ages)
index = ages.index[oldest_age]
team_leader = names[index]
print(team_leader)
组件:
import ToDoEntries from './to_do_entries.jsx';
class ToDoList extends React.Component {
constructor(props) {
super(props);
this.state = { list: [] }
this.add = this.addItem.bind(this);
this.removeItem = this.removeItem.bind(this);
}
addItem(e) { //removed to avoid tl:dr }
render() {
return(
<form onSubmit={this.add}>
<input placeholder='Enter item' type='text' ref={(el) => {this._input = el;} }/>
<button>Add</button>
</form>
<ToDoEntries entries={this.state.list}
removeCallback={this.removeItem}
/>
);
}
}
运行此代码带来:
警告:setState(...):在现有状态期间无法更新 过渡
问题:
为什么to_do_entries.jsx
的渲染会在添加项目时立即执行回调,即:
class ToDoEntries extends React.Component {
constructor(props) {
super(props);
}
renderItems() {
const { entries, removeCallback } = this.props;
function createTasks(item) {
return <li key={item.key}>{item.text}</li>
}
var listItems = entries.map(function(item) {
return(<li onClick={removeCallback} key={item.key}>{item.text}</li>)
})
return listItems;
}
render() {
var todoEntries = this.renderItems();
return(
<ul>
{todoEntries}
</ul>
);
}
}
export default ToDoEntries;
然而,将to_do_entries.jsx
添加到removeCallback即。 var listItems = entries.map(function(item) {
return(<li onClick={removeCallback(id)} key={item.key}>{item.text}</li>)
})
没有?
答案 0 :(得分:4)
问题出在这一部分:
onClick={removeCallback(id)}
我们需要将函数传递给onClick,而不是值。当我们将()
与functionName一起使用时,这意味着您正在调用该方法并将其结果分配给onClick
,如果您执行setState
,则会创建无限循环在removeCallback中,因为这个循环:
render -> removeCallback() -> setState ->
^ |
| |
| |
-----------------------------------------
这就是你收到错误的原因。
检查代码段,了解abc and abc()
:
function abc(){
return 'Hello';
}
console.log('without () = ', abc); //will return the function
console.log('with () = ', abc()); //will return the function result (value)
&#13;
为何使用
onClick={removeCallback.bind(null, id)}
?
因为bind会创建一个新函数,并将该函数指定给click事件,所以当你不自动点击任何项目时,{@ 1}}会被调用。
根据 MDN Doc :
bind()函数创建一个新的绑定函数(BF)。 BF是一个 外来的功能对象(来自ECMAScript 2015的术语)包装了 原始功能对象。调用BF通常会导致 执行包装函数。
检查 React DOC: Handling events in JSX 。
检查此答案以获取有关绑定的更多详细信息: Use of the JavaScript 'bind' method
答案 1 :(得分:2)
我会反对这一点,并对我为您编写的示例使用类似的方法。渲染绑定到状态的待办事项列表,然后将相关信息传递回父组件以删除该项目。在这种情况下,我使用todo的索引来拼接数组,以便将其删除。
当呈现每个todo <li>
时,会立即调用当前的onClick,因为它只是一个导致问题的函数调用。 .bind
解决了这个问题,因为它会在你单击元素时创建一个新函数,这就是函数不立即调用的原因。
但是,这通常被认为是不好的做法,因为每次组件都会一次又一次地创建这个功能。通过屏幕上的todo数量将其倍增,您将失去性能。这是一个小问题,但我的例子展示了如何解决这个问题。 https://codepen.io/w7sang/pen/VWNLJp
// App
class App extends React.Component{
constructor(props) {
super(props);
this.state = { list: [] }
this.add = this.addItem.bind(this);
this.removeItem = this.removeItem.bind(this);
}
addItem(e) {
e.preventDefault();
this.setState({
list: [
...this.state.list,
{
key: Math.random(1,10000),
text: this._input.value
}
]
})
}
removeItem(payload){
this.setState({
list: [
...this.state.list.slice(0, payload.index),
...this.state.list.slice(payload.index + 1)
]
})
}
render() {
return(
<div>
<form onSubmit={this.add}>
<input placeholder='Enter item' type='text' ref={(el) => {this._input = el;} }/>
<button>Add</button>
</form>
<ToDoEntries entries={this.state.list} removeItem={this.removeItem} />
</div>
);
}
}
// TodoEntries [STATELESS]
const ToDoEntries = ({ entries, removeItem } ) => {
return(
<ul>
{ entries.map((item, index) => {
return(<Todo key={item.key} index={index} item={item} removeItem={removeItem} />)
}) }
</ul>
);
}
// Todo
class Todo extends React.Component {
constructor(props){
super(props);
this.state = {};
this.remove = this.remove.bind(this);
}
remove() {
const { index, removeItem } = this.props;
removeItem({
index
});
}
render() {
return <li onClick={this.remove}>{this.props.item.text}</li>
}
}
ReactDOM.render(<App />,document.getElementById('app'));
<div id="app"></div>
答案 2 :(得分:2)
为什么to_do_entries.jsx的渲染会立即执行回调?
好吧,当您对todo每个<li/>
列表的映射调用removeCallback
函数而不是将其分配给onClick
时。
当前代码
<li onClick={removeCallback(id)} </li>
相当于:
var result = removeCallback(id);
<li onClick={result} </li>
您已正确指出使用bind
会有效。这是由于这种行为使得它在这些情况下非常有用。
See the mdn docs for more info,但我会在这里引用重要部分:
bind
...创建并返回一个新功能,在调用时...
在你的情况下,当使用bind,并将它赋给onClick
时,你正在创建一个新函数,当实际触发click事件时将调用该函数,而不是在渲染元素时。
查看removeCallback.bind(null, id)
的另一种方式是这样的:
var newFunc = () => {
return removeCallback(id);
}
<li onClick={newFunc} </li>