React/Redux -为一个组件添加排序或筛选功能



我构建了一个react-redux应用程序,它从Rails API获取评论列表——我真的想添加按字母升序/降序排序评论列表的功能,但我不确定从哪里开始。

我是否需要添加我想如何将这个列表排序到状态,然后添加一个新的操作来做到这一点?或者是否有更简单的方法将其添加到显示评论列表的组件中?

如果可能的话,我喜欢确切的代码示例,因为我真的不知道如何开始以及在哪里可以完成它。

。/组件/评论/ReviewsList.js

import React from 'react'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom'
const ReviewList = props => {
const reviewCards = props.reviews.length > 0 ?
props.reviews.map(r => (
<div>
<br></br>
<p key={r.id}><Link className="App-link" to={`/app/v1/reviews/${r.id}`}>{r.attributes.title}</Link>
</p>
</div>)) :
null

return reviewCards
}
const mapStateToProps = state => {
return {
reviews: state.reviews.reviews
}
}
export default connect(mapStateToProps)(ReviewList)

App.js

import React from 'react';
import './App.css';
import ReviewsContainer from './containers/ReviewsContainer';
import ReviewList from './components/reviews/ReviewList'
import NewReviewForm from './components/reviews/NewReviewForm'
import Review from './components/reviews/Review'
import Header from './components/Header'
import Home from './components/Home'
import Footer from './components/Footer'
import { Switch, Route, withRouter } from 'react-router-dom'
import { connect } from 'react-redux';
class App extends React.Component {
render(){
const { reviews } = this.props
return (
<div className="App">
<Header />
<ReviewsContainer />
<Switch>
{/* Route renders appropriate components with the link path */}
<Route exact path='/' component={Home}></Route> 
<Route exact path='/app/v1/reviews' component={ReviewList}></Route>
<Route exact path='/app/v1/reviews/new' component={NewReviewForm}/>
<Route exact path='/app/v1/reviews/:id' render={props => {
const review = reviews.find(review => review.id === props.match.params.id )
return <Review review={review} {...props}/>
}
// above renders the specific Review URL to the appropriate selection -
}/>
</Switch>
<br></br>
<Footer />
</div>
);
}
}
const mapStateToProps = state => {
return ({
reviews: state.reviews.reviews
})
}
export default withRouter(connect(mapStateToProps)(App))

store.js

import { createStore, applyMiddleware, compose, combineReducers } from 'redux'
import reviews from './reducers/reviewReducer'
import newReviewForm from './reducers/newReviewForm'
import thunk from 'redux-thunk'
const reducers = combineReducers({
reviews,
newReviewForm
})

const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducers, composeEnhancer(applyMiddleware(thunk)))
export default store

如果你想看其他代码,请告诉我。

根据你帖子的措辞,我假设props.reviews包含整个评论列表,而不仅仅是第一页,并且我们不会因为排序而显示不同的子集。如果是这种情况,那么我的建议是保留redux的排序,并通过本地状态或react路由器在组件本身中处理它。

本地状态

这取决于你想使用什么样的格式为你的状态,因为有多种方法是有意义的。

这里我们保存string"asc""desc":

const [order, setOrder] = useState("asc");

这里我们保存一个boolean。如果是true,顺序是降序,如果是false,顺序是升序。

const [isDesc, setIsDesc] = useState(false);

如果您希望支持按title以外的属性排序,则可以使用除顺序之外的第二个状态:

const [sortBy, setSortBy] = useState("title");

路由器var

使用URL查询(如"/app/v1/reviews/?sort=title&order=asc")通过react-router控制排序也可能是有意义的。你可以在react-router文档中看到这种方法的一个例子。

Lodash排序

Lodash有sortByreverse方法,使它超级容易排序你的数组的审查对象。

const sorted = _.sortBy(props.reviews, 'attributes.title');
const reviews = order === "desc" ? _.reverse(sorted) : sorted;

Pure JS Sort

如果你不想依赖于包,你可以传递一个自定义的比较函数给内置的数组排序方法。由于sort()会改变数组,因此您将希望在副本上执行此操作。

您可以在attributes.titlestring上使用localeCompare。在底层,比较使用number,因此我们可以将结果乘以-1来颠倒顺序。

const reviews = [...props.reviews].sort( (a, b) => 
(order === "desc" ? -1 : 1 ) * a.attributes.title.localeCompare(b.attributes.title)
)

与lodash版本相比,这更难读,但同样简洁。

组件代码我重写了您的ReviewList组件,以便它对结果进行排序,并且还包括一个下拉菜单以更改排序顺序。这是一个工作演示(非常愚蠢的虚拟数据)。

const ReviewList = (props) => {
const [order, setOrder] = useState("asc"); // either "asc" or "desc"
const reviews = [...props.reviews].sort(
(a, b) =>
(order === "desc" ? -1 : 1) *
a.attributes.title.localeCompare(b.attributes.title)
);
return (
<div className="review-list">
<div className="sort-order">
<label htmlFor="reviews-sort-order">Order: </label>
<select
id="reviews-sort-order"
value={order}
onChange={(e) => setOrder(e.currentTarget.value)}
disabled={reviews.length === 0}
>
<option value="asc">A-Z</option>
<option value="desc">Z-A</option>
</select>
</div>
{reviews.length === 0 ? (
<div className="no-reviews-found">No Reviews Found</div>
) : (
<ul>
{reviews.map((r) => (
<li key={r.id}>
<Link className="App-link" to={`/app/v1/reviews/${r.id}`}>
{r.attributes.title}
</Link>
</li>
))}
</ul>
)}
</div>
);
};

最新更新