我是ReactJS新手(嗯,具体我是编程新手)寻找一些答案,请不要因为明显的愚蠢而排除我:)我在传递道具和理解'这个'时遇到了很大麻烦上下文。
这是我的混乱,两个简单的Todo应用程序组件。 TodoApp:
import React from 'react';
import uuid from 'uuid';
import style from './App.css';
import Title from '../components/Title.js';
import TodoList from '../components/TodoList.js';
class App extends React.Component {
constructor(props) {
super(props);
this.title = "Todo Application"
this.state = {
data: [{
id: 1,
text: 'clean room'
}, {
id: 2,
text: 'wash the dishes'
}, {
id: 3,
text: 'feed my cat'
}]
};
}
removeTodo(id) {
const remainder = this.state.data.filter(item => item.id !== id);
this.setState({data: remainder});
}
render() {
return (
<div className={style.TodoApp}>
<Title title="Todo Application" added={this.state.data.length} />
<TodoList data={this.state.data} remove={this.removeTodo}></TodoList>
</div>
);
}
}
export default App;
和TodoList:
import React from 'react';
const TodoList = props => (
<ul>
{props.data.map((item, index) =>
<li key={index}>
{item.text}
<button value={index} onClick={() => props.remove(index)}>x</button>
</li>
)}
</ul>
);
export default TodoList;
我的问题是如何正确地将道具传递给子组件(TodoList),以便删除按钮可以工作?
答案 0 :(得分:1)
要确保this
始终引用定义了removeTodo
方法的App上下文,您可以在构造函数中添加以下内容(在设置初始状态之后):
this.removeTodo = this.removeTodo.bind(this);
否则,你的代码根本就不凌乱。它甚至令人惊讶地简洁和深思熟虑,对于一个自称为初学者来说更是如此。恭喜!
正如Sag1v在下面的评论中指出的那样,您还没有将正确的值传递给removeTodo
方法。您正在传递正在迭代的项目的索引,而不是其ID。
将您的<button>
调用更改为以下内容:
<button value={index} onClick={() => props.remove(item.id)}>x</button>
请注意,您也可以通过以下方式实现相同目标:
<button value={index} onClick={props.remove.bind(null, item.id)}>x</button>
答案 1 :(得分:1)
这里有两个主要问题:
正如Jaxx所说,你没有将处理程序removeTodo
绑定到
class
有几种方法可以做到这一点。
将其绑定在constructor
:
this.removeTodo = this.removeTodo.bind(this);
或者使用将this
使用词汇上下文的ES2015(ES6) Arrow functions:
removeTodo = (id) => {
const remainder = this.state.data.filter(item => item.id !== id);
this.setState({data: remainder});
}
第二个问题是你在TodoList
onClick
内部处理
没有将正确的id
传递给处理程序,你正在传递
指数位置。
onClick={() => props.remove(index)}
您应该将其更改为:
onClick={() => props.remove(item.id)}
这种方法还有另一个问题,我将在下面解释。
这是一个有效的例子:
const Title = ({title}) => <h1>{title}</h1>
const TodoList = props => (
<ul>
{props.data.map((item, index) =>
<li key={index}>
{item.text}
<button value={index} onClick={() => props.remove(item.id)}>x</button>
</li>
)}
</ul>
);
class App extends React.Component {
constructor(props) {
super(props);
this.title = "Todo Application"
this.state = {
data: [{
id: 1,
text: 'clean room'
}, {
id: 2,
text: 'wash the dishes'
}, {
id: 3,
text: 'feed my cat'
}]
};
}
removeTodo = (id) => {
const remainder = this.state.data.filter(item => item.id !== id);
this.setState({data: remainder});
}
render() {
return (
<div>
<Title title="Todo Application" added={this.state.data.length} />
<TodoList data={this.state.data} remove={this.removeTodo}></TodoList>
</div>
);
}
}
ReactDOM.render(<App/>,document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
正如我所说的,这种方法存在问题,您使用以下代码行在每个渲染上传递一个函数的新实例:
onClick={() => props.remove(item.id)
这被认为是不好的做法,因为这会中断Reconciliation and diffing algorithm
但是正如我们所知,event
处理程序应该获得一个函数引用,因此你不能只传递一个像这样的函数调用
onClick={props.remove(item.id)}
这将传递函数的返回类型(如果有),而不是函数的引用 所以传递函数引用的正确方法是这样的:
onClick={props.remove}
但这对你的情况不利,因为你需要将当前项id
传回给父,但我担心浏览器只会传回event
参数。< / p>
那么你问的替代方案是什么?
创建另一个组件并控制您从组件传入和传出的数据,而不是依赖于浏览器的商誉。
这是另一个工作示例,但这次没有在每个渲染上创建一个新的函数实例
const Title = ({title}) => <h1>{title}</h1>
class TodoItem extends React.Component {
handleClick = () => {
const{item, onClick} = this.props;
onClick(item.id);
}
render(){
const {item} = this.props;
return(
<li>
{item.text}
<button value={item.id} onClick={this.handleClick}>x</button>
</li>
);
}
}
const TodoList = props => (
<ul>
{props.data.map((item, index) =>
<TodoItem key={index} item={item} onClick={props.remove} />
)}
</ul>
);
class App extends React.Component {
constructor(props) {
super(props);
this.title = "Todo Application"
this.state = {
data: [{
id: 1,
text: 'clean room'
}, {
id: 2,
text: 'wash the dishes'
}, {
id: 3,
text: 'feed my cat'
}]
};
}
removeTodo = (id) => {
const remainder = this.state.data.filter(item => item.id !== id);
this.setState({data: remainder});
}
render() {
return (
<div>
<Title title="Todo Application" added={this.state.data.length} />
<TodoList data={this.state.data} remove={this.removeTodo}></TodoList>
</div>
);
}
}
ReactDOM.render(<App/>,document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>