React将函数应用于数组

时间:2017-11-30 23:19:30

标签: arrays reactjs

以下代码创建了一个简单的电影评级应用。除了在其中一个数组项中单击向上或向下投票时,所有内容都有效,投票表示数组更新中的所有项目,而不仅仅是单击的项目。如何编码,以便投票仅适用于点击它的项目?

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')
);

2 个答案:

答案 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>