以下代码创建了一个简单的电影评级应用。除了在其中一个数组项中单击向上或向下投票时,所有内容都有效,投票表示数组更新中的所有项目,而不仅仅是单击的项目。如何编码,以便投票仅适用于点击它的项目?
class Ratings extends React.Component {
constructor(props){
super(props);
this.state = {
votes: 0
};
this.add = this.add.bind(this);
this.subtract = this.subtract.bind(this);
this.reset = this.reset.bind(this);
}
add(event){
this.setState ({
votes: this.state.votes + 1
})
}
subtract(event){
this.setState ({
votes: this.state.votes - 1
})
}
reset(event){
this.setState ({
votes: 0
})
}
render () {
this.movies = this.props.list.map(x => {
return (
<div key={x.id} className="movierater">
<MoviePoster poster={x.img}/>
<h1 className="title">{x.name}</h1>
<div className="votewrapper">
<button onClick={this.add}><i className="votebutton fa fa-thumbs-o-up" aria-hidden="true"></i></button>
<Votes count={this.state.votes} />
<button onClick={this.subtract}><i className="votebutton fa fa-thumbs-o-down" aria-hidden="true"></i></button>
</div>
<button onClick={this.reset} className="reset">Reset</button>
</div>
)
});
return (
<div>
{this.movies}
</div>
);
}
}
function MoviePoster(props) {
return (
<img src={props.poster} alt="Movie Poster" className="poster"/>
);
}
function Votes(props) {
return (
<h2>Votes: {props.count}</h2>
);
}
var movieposters = [
{id: 1, img:"http://www.impawards.com/2017/posters/med_alien_covenant_ver4.jpg", name: "Alien Covenant"},
{id: 2, img:"http://www.impawards.com/2017/posters/med_atomic_blonde_ver4.jpg", name: "Atomic Blonde"},
{id: 3, img:"http://www.impawards.com/2017/posters/med_easy_living_ver3.jpg", name: "Easy Living"},
{id: 4, img:"http://www.impawards.com/2017/posters/med_once_upon_a_time_in_venice_ver3.jpg", name: "Once Upon a Time in Venice"},
{id: 5, img:"http://www.impawards.com/2017/posters/med_scorched_earth.jpg", name: "Scorched Earth"},
{id: 6, img:"http://www.impawards.com/2017/posters/med_underworld_blood_wars_ver9.jpg", name: "Underworld: Blood Wars"},
{id: 7, img:"http://www.impawards.com/2017/posters/med_void.jpg", name: "The Void"},
{id: 8, img:"http://www.impawards.com/2017/posters/med_war_for_the_planet_of_the_apes.jpg", name: "War for the Planet of the Apes"},
]
ReactDOM.render(
<Ratings list={movieposters} />,
document.getElementById('app')
);
答案 0 :(得分:1)
你需要跟踪每个元素的投票,因此this.state.votes +/- 1不能完成这项工作,所以:
更改
this.state = {
votes: 0
}
到
this.state = {
votes: {}
}
然后更改功能:
add(id){
return function(event) {
this.setState ({ ...this.state.votes, [id]: parseInt(this.state.votes[id]) + 1 })
}
}
和减去相同。然后将按钮更改为:
<button onClick={this.add(x.id)} ... (same for subtract)
并最后更改您的投票组件:
<Votes count={this.state.votes[x.id] || 0} />
重置时,请执行以下操作:
reset(event){
this.setState ({ votes: {} })
}
答案 1 :(得分:1)
每个电影实体都需要单独的投票计数 这可以通过为每部电影提供id并通过id设置该特定电影的投票来实现。
我还建议为Movie
提取新组件
该组件将获得movieId
作为prop和处理程序,它将调用向上或向下处理程序并向它们提供当前movieId
。
查看正在运行的示例:
class Movie extends React.Component {
onSubtract = () => {
const { subtract, movieId } = this.props;
subtract(movieId);
};
onAdd = () => {
const { add, movieId } = this.props;
add(movieId);
};
onReset = () => {
const { reset, movieId } = this.props;
reset(movieId);
};
render() {
const { movie, votes = 0 } = this.props;
return (
<div className="movierater">
<MoviePoster poster={movie.img} />
<h1 className="title">{movie.name}</h1>
<div className="votewrapper">
<button onClick={this.onAdd}>
<i className="votebutton fa fa-thumbs-o-up" aria-hidden="true" />
</button>
<Votes count={votes} />
<button onClick={this.onSubtract}>
<i className="votebutton fa fa-thumbs-o-down" aria-hidden="true" />
</button>
</div>
<button onClick={this.onReset} className="reset">
Reset
</button>
</div>
);
}
}
class Ratings extends React.Component {
constructor(props) {
super(props);
this.state = {
allVotes: {}
};
}
subtract = movieId => {
const { allVotes } = this.state;
const currentVote = allVotes[movieId] || 0;
const nextState = {
...allVotes,
[movieId]: currentVote - 1
};
this.setState({allVotes: nextState});
};
add = movieId => {
const { allVotes } = this.state;
const currentVote = allVotes[movieId] || 0;
const nextState = {
...allVotes,
[movieId]: currentVote + 1
};
this.setState({ allVotes: nextState });
};
reset = movieId => {
const { allVotes } = this.state;
const nextState = {
...allVotes,
[movieId]: 0
};
this.setState({ allVotes: nextState });
};
render() {
const { allVotes } = this.state;
this.movies = this.props.list.map(x => {
const votes = allVotes[x.id];
return (
<Movie
movieId={x.id}
movie={x}
votes={votes}
reset={this.reset}
subtract={this.subtract}
add={this.add}
/>
);
});
return <div>{this.movies}</div>;
}
}
function MoviePoster(props) {
return <img src={props.poster} alt="Movie Poster" className="poster" />;
}
function Votes(props) {
return <h2>Votes: {props.count}</h2>;
}
var movieposters = [
{
id: 1,
img: "http://www.impawards.com/2017/posters/med_alien_covenant_ver4.jpg",
name: "Alien Covenant"
},
{
id: 2,
img: "http://www.impawards.com/2017/posters/med_atomic_blonde_ver4.jpg",
name: "Atomic Blonde"
},
{
id: 3,
img: "http://www.impawards.com/2017/posters/med_easy_living_ver3.jpg",
name: "Easy Living"
},
{
id: 4,
img:
"http://www.impawards.com/2017/posters/med_once_upon_a_time_in_venice_ver3.jpg",
name: "Once Upon a Time in Venice"
},
{
id: 5,
img: "http://www.impawards.com/2017/posters/med_scorched_earth.jpg",
name: "Scorched Earth"
},
{
id: 6,
img:
"http://www.impawards.com/2017/posters/med_underworld_blood_wars_ver9.jpg",
name: "Underworld: Blood Wars"
},
{
id: 7,
img: "http://www.impawards.com/2017/posters/med_void.jpg",
name: "The Void"
},
{
id: 8,
img:
"http://www.impawards.com/2017/posters/med_war_for_the_planet_of_the_apes.jpg",
name: "War for the Planet of the Apes"
}
];
ReactDOM.render(<Ratings list={movieposters} />, document.getElementById("root"));
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<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>