This is the BookList
component to re-render on the filter.sortBy
prop change.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { getBooks } from '../actions/bookActions';
import Spinner from './Spinner';
import BookItem from './BookItem';
import sortBooks from '../selectors/books';
class BookList extends Component {
componentDidMount() {
this.props.getBooks();
}
render() {
const { books, loading } = this.props;
let booksContent;
if (!books || loading) {
booksContent = <Spinner />;
} else {
if (books.length > 0) {
booksContent = books.map(book => <BookItem book={book} key={book._id} />);
} else {
booksContent = <h4>No books found</h4>;
}
}
return (
<div className='feed'>
<div className='container'>
<div className='row'>
<div className='col-md-12'>
{booksContent}
</div>
</div>
</div>
</div>
);
}
}
BookList.propTypes = {
books: PropTypes.oneOfType([PropTypes.array, PropTypes.object]).isRequired,
getBooks: PropTypes.func.isRequired
};
const mapStateToProps = (state) => {
const books = sortBooks(state.books.books, state.filter.sortBy);
return {
books: books
}
};
export default connect(mapStateToProps, { getBooks })(BookList);
There is another component to change the state.filter.sortBy
value. From what got logged inside mapStateToProps()
above state.filter.sortBy
changes correctly.
How do I get BookList
to re-render when I change the sortBy
value? The complete repo is on https://github.com/ElAnonimo/booklist So far I've tried a constructor()
, a componentWillReceiveProps()
in BookList
.
UPDATE.
This is what my sortBooks
selector looked like
export default (books, sortBy) => {
return books
.sort((a, b) => {
if (sortBy === 'title') {
return a.title < b.title ? -1 : 1;
} else if (sortBy === 'releasedAt') {
return a.releasedAt < b.releasedAt ? -1 : 1;
}
});
};
As was mentioned in the accepted answer that way the selector returned an internally modified but still the original books
array to the BookList
component. Which resulted in no props change for Redux.
This is how I got it to return a modified copy of the original array.
export default (books, sortBy) => {
return [...books]
.sort((a, b) => {
if (sortBy === 'title') {
return a.title < b.title ? -1 : 1;
} else if (sortBy === 'releasedAt') {
return a.releasedAt < b.releasedAt ? -1 : 1;
}
});
};
The credit goes to Ryan C.
UPDATE 2. Another credit to Ryan C. Thank you for sharing the wisdom.
Here are his suggested modifications applied.
booksReducer
. Before it lacked the SORT_BY_TITLE
and SORT_BY_RELEASED_AT
cases.
import {
GET_BOOKS,
BOOKS_LOADING,
DELETE_BOOK,
SORT_BY_TITLE,
SORT_BY_RELEASED_AT
} from '../actions/types';
const initialState = {
books: []
};
export default function(state = initialState, action) {
switch(action.type) {
case BOOKS_LOADING:
return {
...state,
loading: true
};
case GET_BOOKS:
return {
...state,
books: action.payload,
loading: false
};
case DELETE_BOOK:
return {
books: [...state.books.filter(book => book._id !== action.payload.id)]
};
case SORT_BY_TITLE:
return {
...state,
books: [...state.books.sort((a, b) => a.title < b.title ? -1 : 1 )]
};
case SORT_BY_RELEASED_AT:
return {
...state,
books: [...state.books.sort((a, b) => a.releasedAt < b.releasedAt ? -1 : 1 )]
};
default:
return state;
}
}
The modification to the BookList
const mapStateToProps = (state) => ({
books: state.books.books
});
These changes effectively leave out the need for a selector in the project.
I suspect that your sortBooks
method is sorting the array in place, such that the same books
array reference is always returned (even though the order of the elements within it may have been changed). This causes react-redux to think that the properties returned by mapStateToProps
have not changed (react-redux only does a shallow comparison). If you change sortBooks
to first duplicate the array and then sort it and return the new array rather than mutating the existing array, it should work.
UPDATE After seeing your resolution, I think it would be better to handle this sorting in your reducer since the books array is in state. This way you can control only doing the sorting in response to the action that changes the sort. The way it is now, you will end up re-rendering the book list on every state change (even if the sort did not change) since mapStateToProps will ALWAYS return a different books array.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.