Массивы как свойства компонентов react


Сейчас данные о пунктах списка дел хранит TodoList в свойствах label и important

const TodoList = () => (
    <ul>
      <li><TodoListItem label='Learn JS' important/></li>
      <li><TodoListItem label='Create awesome app'/></li>
    </ul>
  );

Это не совсем правильный подход. Задача списка дел - отображать данные, а получать и хранить их значения и важность лучше в основном файле проекта index.js в компоненте App. При таком подходе, если нам понадобится изменить список дел, не нужно будет искать его по всем файлам проекта, достаточно будет внести изменения в основной файл.

const App = () => {
   const todoDate = [
    {label: 'Learn JS', important: true},
    {label: 'Create awesome app', important: true},
    {label: 'Drink coffee', important: false}
  ];

   return (
     <div>
      <AppHeader/>
      <SearchPanel/>
      <TodoList todos={todoDate}/>
    </div>
  );
};

Мы создали массив todoDate, элементами его являются объекты с ключами label и important и добавили этот объект в качестве свойства компоненту TodoList.

Передать данные из  в TodoList можно было бы так

const TodoList = ({todos}) => (
    <ul>
      <li><TodoListItem label={todos[0].label} important={todos[0].important}/></li>
      <li><TodoListItem label={todos[1].label} important={todos[1].important}/></li>
      <li><TodoListItem label={todos[2].label} important={todos[2].important}/></li>
    </ul>
  );

Такой подход работающий, но наивный и не оптимальный. Перебирать массив по одному элементу не совсем правильно.
Оптимизируем код выше:

const TodoList = ({todos}) => {
  const elements = todos.map(item => {
    return <li><TodoListItem label={item.label} important={item.important}/></li>
  });

  return (
    <ul>
      {elements}
    </ul>
  );
}

Можно ещё немного сократить код, использовав spread-оператор для объекта. Если имена свойств компонента совпадают с именами свойств объекта, нет необходимости перечислять их, достаточно использовать оператор расширения

В результате вот такая строка

<TodoListItem label={item.label} important={item.important}/>

превратится вот в такую

<TodoListItem {...item}/>

Этот код работает, но в консоли появились ошибки. Дело в том, что для каждого элемента списка реакт ожидает уникальный идентификатор. такой идентификатор нужен для того, чтобы, когда список изменится, реакт мог отследить изменения и обновить только изменившийся элемент, а не весь список целиком. В качестве идентификатора может использоваться порядковый номер элемента в списке, но такой подход нерациональный - дело в том, что сам реакт использует точно такой же подход и сработает он только в том случае, если новые элементы будут добавляться исключительно в конец списка.

Исправить можно так
App:

const App = () => {
   const todoDate = [
    {label: 'Learn JS', important: true, id: 1},
    {label: 'Create awesome app', important: true, id: 2},
    {label: 'Drink coffee', important: false, id: 3}
  ]; 
  
   return ( 
     <div>
      <AppHeader/>
      <SearchPanel/>
      <TodoList todos={todoDate}/>
    </div>
  );
};

TodoList:

const TodoList = ({todos}) => {
  const elements = todos.map(item => {
    return (
      <li key={item.id}>
        <TodoListItem {...item}/>
      </li>
      );
  });
  
  return (
    <ul>
      {elements}
    </ul>
  );
}

Или так:

const TodoList = ({todos}) => {
  const elements = todos.map(item => {
    const {id, ...itemProps} = item;
    return (
      <li key={id}>
        <TodoListItem {...itemProps}/>
      </li>
      );
  });
  
  return (
    <ul>
      {elements}
    </ul>
  );
}


Или так:

const TodoList = ({todos}) => {
  const elements = todos.map((item, index) => {
    return (
      <li key={index}>
        <TodoListItem {...item}/>
      </li>
      );
  });

  return (
    <ul>
      {elements}
    </ul>
  );
}