21

I currently render a list in the typical React-style. The list is passed as an array prop, and I map over it like so:

{this.props.myList.map(createListItem, this)}

So when a new element is added, it appears like the latest item was added to the end of the list.

I would like it so the latest item appears at the top. i.e. everything appears in reverse-chronological order.

The two options I've come up with so far are: 1) Reverse the list, creating a new array each time something is added, and pass this reversed list as the prop. 2) Use shift.

But they're both unappealing because of performance.

I'm not aware of Javascript supporting mapping in reverse order. I've been trying a for-loop but I haven't been able to get it to work.

What is the idiomatic way to render an array in reverse order in React?

MarkPickett
  • 221
  • 1
  • 2
  • 3
  • Is this.props.myList.reverse().map an option? – Jeremy Gordon Jun 06 '16 at 18:10
  • i think using native reverse() method is not a bad idea. in Google Chrome array.reverse is faster than the other methods But, if performance is really a concert, you can use approaches described in the answer here http://stackoverflow.com/questions/5276953/what-is-the-most-efficient-way-to-reverse-an-array-in-javascript – nuway Jun 06 '16 at 18:13
  • I think you might be overthinking this. Both of the methods you suggested will run on even large arrays in < 1 ms. Both are far more performant than the `map` you're already using. – Jake Haller-Roby Jun 06 '16 at 18:47

8 Answers8

45

If you choose to reverse the list using reverse(), shift() or splice(), you should make a shallow copy of the array first, and then use that function on the copy. Props in React should not be mutated.

For example:

[...this.props.myList].reverse().map(createListItem, this)

or

this.props.myList.slice(0).map(createListItem, this)

(this should really be a comment, but I don't have the points to do that yet :))

Mohammad Kanan
  • 4,452
  • 10
  • 23
  • 47
nutsandbolts
  • 523
  • 4
  • 8
  • 2
    worked in 4 seconds. I needed more time to write this comment! thanks! – user3417479 Sep 03 '19 at 15:48
  • Yes thanks you very much ! I got a blink problem with an array of pictures and you save my design ! – E. Spiroux Sep 05 '19 at 15:29
  • You Helped me with this code. Thank you. `this.state.ALLProducts.reverse().map((ALLProducts) =>` – Mohamed Raza Jul 24 '20 at 13:03
  • This method also works with state ```React.useState((current)=>[...current].reverse())``` If you do not use the spread operator it does not work. ("tested" with an array of objects) – Daniel Dec 24 '20 at 21:26
34

If you need to display a list in the UI in reverse order you can also use

flex-direction: row-reverse;

or

flex-direction: column-reverse;
Alfrex92
  • 6,278
  • 9
  • 31
  • 51
4

As others have pointed out the humble reverse method does the job for most part. I currently ran into the same issue and I must say that using array.reverse() atleast in Chrome, the performance hit wasnt seen as such. In my opinion its better than using a loop to sort a list in reverse order.

 array.reverse()
SeaWarrior404
  • 3,811
  • 14
  • 43
  • 65
1

When using mobx as a store one can create a computed property for reversed array that will be reevaluated and memoized each time original observable array changes.

Store

import { observable, computed } from 'mobx';

class MyStore {
  @observable items = [1, 2, 3];

  @computed get itemsReversed() {
    return this.items.slice().reverse();
  }
}

export default MyStore;

Rendering

import React, { Component } from 'react';
import { inject, observer } from 'mobx-react';

@inject('myStore') @observer
class List extends Component {
  render() {
    const { myStore } = this.props;
    const { itemsReversed } = myStore;
    return (
      <div className="list">
        {itemsReversed.map(item => (
          <div className="list-item">{item}</div>
        ))}
      </div>
    );
  }
}

export default List;

According to the official documentation this is a preferred way to reverse an array:

Unlike the built-in implementation of the functions sort and reverse, observableArray.sort and reverse will not change the array in-place, but only will return a sorted / reversed copy. From MobX 5 and higher this will show a warning. It is recommended to use array.slice().sort() instead.

ezze
  • 3,933
  • 2
  • 34
  • 58
1

Some how while using array.reverse() the order was changing whenever something in state changed.I went with flexDirection:'column-reverse' which worked fine and you dont need to mess with the array data aswell.

Sjonchhe
  • 790
  • 8
  • 16
0

Add the new elements at the beginning of the array:

array.splice(0,0,'value to add at beginning');

Or call a for loop with an immediately invoked function:

{(() => {
     for(...) {
         return (<i>{whatever}</i>)
     }
})()}
vcanales
  • 1,818
  • 16
  • 20
0

Keep pushing at the array, and when rendering, you can simply use the

Array.reverse()

here the documentation

Remind that it will mutate the original one

Michael Rasoahaingo
  • 1,069
  • 6
  • 11
0

Simply first create copy of array using slice and apply reverse function when using map. For example:

var myArr = [1,2,3,4,5]
myArr.slice(0).reverse().map((element, index) => {
  console.log(element);
});