Reputation: 1685
So I am mapping an array to a list group to display a list of items. I want to be able to sort that list by any property of the items. I am able to resort the array easy enough, and I have a Reactstrap dropdown that triggers the sort, and the arrays sorts properly and even gets updated in the component properly. However, the component does not re-render. HOWEVER, if I click the button to open the dropdown list again, the component THEN re-renders. I'm baffled. Any thoughts?
Function in the container component file that does the sorting (uses another external utility function, again this works fine):
select(event) {
this.setState({
sortBy: event.target.innerText
}, function () {
const propName = this.state.sortBy.toLowerCase();
this.state.dives.sort(Utilities.sortByParam(propName));
});
}
This is the presentational component:
const DiveList = (props) => {
const {
dives,
toggle,
select,
isOpen,
} = props;
console.log('dives: ', dives);
return (
<div>
<h3>My Dives
<Dropdown size="sm" className="float-right" isOpen={isOpen} toggle={toggle}>
<DropdownToggle caret>
Sort By
</DropdownToggle>
<DropdownMenu right>
<DropdownItem onClick={select}>Number</DropdownItem>
<DropdownItem onClick={select}>Location</DropdownItem>
<DropdownItem onClick={select}>Date</DropdownItem>
</DropdownMenu>
</Dropdown>
</h3>
{dives.length > 0 &&
<div>
<ListGroup>
{dives.map((dive) => (
<ListGroupItem key={`dive-${dive.number}`}>
<Link to={`/divedetails/${dive.number}`}>
<div>
<span>Dive #{dive.number}</span>
<span className="float-right">{dive.date}</span>
</div>
<div className="float-left">{dive.location}</div>
</Link>
</ListGroupItem>
))}
</ListGroup>
</div>
}
{dives.length <= 0 &&
<h5>You don't have any dives logged yet. Time to get wet!!!</h5>
}
</div>
);
};
export default DiveList;
Again, the console log "dives" updates the array immediately when the button is selected (clicked) in the dropdown, but the ListGroup component does not re-render.
If I click the "Sort by" button again (NOT one of the menu buttons, but the toggle button) then the components refreshes.
Help...
Upvotes: 0
Views: 1150
Reputation: 1893
The problem is that you're a) sorting in the setState
callback and b) your sort is mutating dives
instead of setting state.dives
to a new, sorted array via setState
. This is because render
isn't called if state
mutates directly - only if setState
is called. Instead you can perform the sort in the select
function and set sortBy
and dives
in a single setState
call.
select(event) {
const sortBy = event.target.innerText;
const sortByParam = sortBy.toLowerCase();
this.setState({
sortBy,
dives: [...this.state.dives.sort(Utilities.sortByParam(sortByParam))];
});
}
Upvotes: 1