Martin Nordström Martin Nordström - 24 days ago 8
React JSX Question

Button removes list items and changes their ordning | React

I have a button that is supposed to toggle more items in a list when pressing it. I set the default state in my constructor:

constructor(props) {
super(props)
this.state = {
showCount: 10,
expanded: false
}
}


So the default number of items in the list will be 10 and it's not expanded. Then I created a function that is supposed to change the state and load more list items:

loadMore() {
const { showCount } = this.state
const { events } = this.props
showCount === 10
? this.setState({ showCount: events.reverse().length, expanded: true })
: this.setState({ showCount: 10, expanded: false })
}


And created a "button" that is supposed to change it's text if the
expanded
state is true or false:

const toggleMore = (
<div className="text-center">
<a className="show-more" onClick={() => this.loadMore()}>
{this.state.expanded ? (
<span>Visa mindre</span>
) : (
<span>Visa mer</span>
)}
</a>
</div>
)


And then the list item component that uses
.slice
with the
showCount
variable:

const mappedEvents =
events.length === 0 ? (
<p style={{ textAlign: 'center' }}>No events</p>
) : (
events
.reverse()
.slice(0, this.state.showCount)
.map((event, i) => {
return (
<div>
<EventItem
key={i}
...
/>
</div>
)
})
)


My Problems



So this button works great the first time I use it. If I come to my page I can see all of the 10 list items and when pressing the button the text changes. But when I press it a second time (when the
expanded: true
) all of my list items disappears. And when I press it the third time the order of my list items changes, meaning that
.revers()
apparently doesn't work anymore. And when I press it the fourth time it goes back to the first stage; 10 list items and the ordning is as it should be. And after that it just keeps repeating.

So I just want to know what I am doing wrong here and why the button acts like this.

Thanks for reading!

Answer Source

The basic problem is that events.reverse() is destructive, it actually mutates the original array, it doesn't simply return a new reversed array. This array is coming from props which is also a problem -- you definitely shouldn't modify something that is coming from props, you don't know if the parent component will later overwrite it with a new array.

Also if you are using reverse() during render, remember render probably runs a whole bunch more times that you might realise. Each time it renders it will reverse the array yet again.

What I would suggest here is only perform the array transform during render, and do things in a non-destructive way. One way to get a new reversed array is to first make a copy with slice and then reverse it, as follows:

const reversed = events.slice().reverse();

Also I note you are using events.reverse().length -- presumably this is the same as events.length, do you need to mutate the array here?