I am working on movie collection app , where i have a page with movie list. I want to add the functionality of filtering the movie based on year, genre and rating and also i want to sort it alphabetically ,year and rating. How can i accomplish that by using redux and react by using single state object.
可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
回答1:
First, we should create component to display movies list.
Note, this is a pure stateless component, we are not making sorting or filtering there, just rendering derived props:
// Movies.js import React from 'react'; function Movies({ movies }) { return ( {movies.map((m, i) => - {m.year} - {m.title}.({m.genre}) - {m.rating}
)}
); } export default Movies;
Next we will create another stateless component, which will contain sorting and filters view.
Here we are also rendering props, and providing callback to select
elements:
// Pane.js import React from 'react'; function Pane({ selectedYear, selectedGenre, selectedRating, years = [], genres = [], ratings = [], sorting, onYearChange, onGenreChange, onRatingChange, onSortingChange, }) { return ( Filters: Year: Genre: Rating: Select sorting: ); } export default Pane;
We will store movies and all filtering and sorting data in app state. We need create reducer, to handle sorting and filtering actions:
// reducers.js const items = [{ title: 'Mad max', year: 2015, rating: 8, genre: 'fantasy', }, { title: 'Spider man 2', year: 2014, rating: 7, genre: 'fantasy', }, { title: 'Iron man 3', year: 2013, rating: 7, genre: 'fantasy', }, { title: 'Dumb and Dumber To', year: 2014, rating: 5, genre: 'comedy', }, { title: 'Ted 2', year: 2015, rating: 6, genre: 'comedy', }]; export default function moviesReducer(state = { movies: items, year: 'all', rating: 'all', genre: 'all', sorting: 'year', }, action) { switch (action.type) { case 'SET_YEAR': return { ...state, year: action.year, }; case 'SET_RATING': return { ...state, rating: action.rating, }; case 'SET_GENRE': return { ...state, genre: action.genre, }; case 'SET_SORTING': return { ...state, sorting: action.sorting, }; default: return state; } }
To provide data to stateless components, we should use containers
.
Let's create PaneContainer
to provide sorting and filtering data to Pane
component:
// PaneContainer.js import { connect } from 'react-redux'; import Pane from './Pane'; // Simple helper function, which return all filters from state by given key. function getFilters(key, movies) { return movies.reduce((acc, movie) => { if (!acc.includes(movie[key])) { return [...acc, movie[key]]; } return acc; }, []); } function mapStateToProps(state, props) { const { sorting, year, genre, rating } = state; return { selectedYear: year, selectedGenre: genre, selectedRating: rating, years: getFilters('year', state.movies), genres: getFilters('genre', state.movies), ratings: getFilters('rating', state.movies), sorting, }; } function mapDispatchToProps(dispatch, props) { return { // Here, we are providing callbacks with dispatching functions. onYearChange(year) { dispatch({ type: 'SET_YEAR', year, }); }, onGenreChange(genre) { dispatch({ type: 'SET_GENRE', genre, }); }, onRatingChange(rating) { dispatch({ type: 'SET_RATING', rating, }); }, onSortingChange(sorting) { dispatch({ type: 'SET_SORTING', sorting, }); }, }; } export default connect( mapStateToProps, mapDispatchToProps )(Pane);
And finally, we will create MoviesContainer
which will provide visible movies to Movies
component:
// MoviesContainer.js import { connect } from 'react-redux'; import Movies from './Movies'; // Getting visible movies from state. function getVisibleMovies(year, genre, rating, sorting, movies) { return movies .filter(m => { return ( (year == 'all' || year == m.year) && (genre == 'all' || genre == m.genre) && (rating == 'all' || rating == m.rating) ); }) .sort((a, b) => { if (sorting == 'year') { return b.year - a.year; } if (sorting == 'rating') { return b.rating - a.rating; } if (sorting == 'alphabetically') { return a.title > b.title ? 1 : a.title