Вопрос: Чем заменить функцию .map() в моем случае?

Лучший ответ: На вопрос ответил автор вопроса, я в свою очередь попытаюсь объяснить что произошло.

Рендер в реакте всегда идёт по наименее трудозатратному пути, а именно вычисляет дельту (разницу) между новым стейтом и предыдущим, если таковой имеется. Например:

class TodoList extends React.Component {
  state = {
    todos: [
      'Commit',
      'Push'
    ]
  }
  render() {
    return <ul>
      {this.state.todos.map(item => {
        return <li>{ todo }</li>
      }
    </ul>
  }
}

Если стейт компонента изменится, скажем при добавление элемента в начало списка todos, так что он станет таким:

const todos = [
  'Init',
  'Commit',
  'Push'
]

Реакт вычислит два древа VirtualDOM:

// Начальный стейт
<ul>
  <li>Commit</li>
  <li>Push</li>
</ul>
// Добавлен элемент
<ul>
  <li>Init</li> // <- разница начинается здесь и до конца древа
  <li>Commit</li>
  <li>Push</li>
</ul>

Здесь выполняется работа которой можно было бы избежать. К примеру если бы элемент был добавлен в конец списка:

const todos = [
  'Commit',
  'Push',
  'Merge'
]

То реакт получил бы на сравнение другие два древа элементов:

// Начальный стейт
<ul>
  <li>Commit</li>
  <li>Push</li>
</ul>
// Добавлен элемент
<ul>
  <li>Commit</li>
  <li>Push</li>
  <li>Merge</li> <- разница начинается здесь, от начала и до сих по ничего не менялось
</ul>

Дельта этих двух списков меньше, а значит и работы нужно сделать меньше.

Совершенно очевидно что <li>Commit</li> и <li>Push</li> не менялись, однако реакт недостаточно умён чтобы это понять. Чтобы помочь ему следует воспользоваться специальным пропом key={}. Он может быть значением любого типа, единственно требование — значение должно стабильно идентифицировать соответствующие данные.

Если бы компонент выглядел так:

class TodoList extends React.Component {
  state = {
    todos: [
      { id: 0, text: 'Commit' },
      { id: 1, text: 'Push' }
    ]
  }
  render() {
    return <ul>
      {this.state.todos.map(item => {
        return <li key={todo.id}>{ todo.text }</li>
      }
    </ul>
  }
}

То добавление элемента в начало массива, породило бы следующий стейт:

const todos = [
  { id: 2, text: 'Init' },
  { id: 0, text: 'Commit' },
  { id: 1, text: 'Push' }
]

И, снова, два древа элементов:

// Начальный стейт
<ul>
  <li>Commit</li> // id 0
  <li>Push</li> // id 1
</ul>
// Добавлен элемент
<ul>
  <li>Init</li> // id 2 новый элемент отобразится в начале
  <li>Commit</li> // id 0
  <li>Push</li> // id 1 
</ul>

Благодаря подсказке, реакт не учтёт в дельте элементы чьи идентификаторы не изменились, и, как следствие, не будет делать лишних действий.

Таким образом использовать индекс в массиве в качестве ключа — не лучшая идея, особенно если массив будет меняться. По той же причине не следует использовать Math.random() в качестве ключа, так вы почти гарантировано будете всегда получать нестабильные идентификаторы.

Подробнее об этом можно почитать здесь — Reconciliation.

Худший ответ: Спасибо Алексей Казаков.Решение:...
render() {
 let keys = {};
 rows.map(row => {
    keys.key = row.number;
 });

 return (
 ...
                <CSVLink
                  key={keys.key}
                  className="btn btn-secondary btn-download"
                  data={rows}
                  separator={";"}
                  filename="netpositions.csv"
                >
                  <span className="oi oi-data-transfer-download" />
                </CSVLink>
 ))}
...
 )
}
Если кто-то предложит другое решение - буду признателен.