Как я могу отсортировать массив объектов из API перед их передачей в компонент реакции?

Привет, я новичок в react/js, и я пытаюсь отсортировать некоторые данные, которые я извлекаю из API, используя Apollo, а затем отображаю данные в сетке. Когда я пытаюсь использовать функцию sort(), я получаю сообщение об ошибке в консоли «TypeError: Попытка изменить настраиваемый атрибут ненастраиваемого свойства». Может ли кто-нибудь помочь объяснить, что я делаю неправильно? Большое спасибо

Это код, который я пробовал. Работает без метода data.ammo.sort.

Я ожидал, что данные будут отображаться в порядке, отсортированном по повреждению

import { useQuery, gql } from '@apollo/client';
import AmmoBox from "./components/Ammo"
import {Grid} from "@chakra-ui/react"

export const GET_AMMO = gql
`{
    ammo{
        item {
            id
            name
            shortName
        }
        damage
        penetrationPower
        caliber
    }
}`

function App() {

  const { loading, error, data } = useQuery(GET_AMMO);

  if (loading) return 'Loading';
  if (error) return 'Error';

  console.info(data.ammo);

  //sort data here before mapping to AmmoBox component*
  data.ammo.sort((a, b) => a.damage - b.damage);

  return (
    <Grid templateColumns='repeat(5, 1fr)' gap = {6}>
      {data.ammo
      .map((ammos) => (
        <AmmoBox name = {ammos.item.name} caliber = {ammos.caliber} penetration = {ammos.penetrationPower} damage = {ammos.damage}/>))}
    </Grid>
  );
}

export default App;
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
0
54
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Также можно использовать useMemo для уменьшения результатов рендеринга и кеша

function App() {

  const { loading, error, data } = useQuery(GET_AMMO);

  if (loading) return 'Loading';
  if (error) return 'Error';

  console.info(data.ammo);

  const sortedData = useMemo(() => {
       const copied = [...data?.ammo ?? [] ]
       return copied.sort((a, b) => a.damage - b.damage)
  }, [])


  return (
    <Grid templateColumns='repeat(5, 1fr)' gap = {6}>
      {sortedData
      .map((ammos) => (
        <AmmoBox name = {ammos.item.name} caliber = {ammos.caliber} penetration = {ammos.penetrationPower} damage = {ammos.damage}/>))}
    </Grid>
  );
}

Надеюсь, поможет

Спасибо! Мне нужно немного прочитать об использовании Memo

nickbeaumont 16.12.2022 00:59
Ответ принят как подходящий

Поскольку ошибка говорит, что вы не можете изменить data.ammo, распространите данные на новый (неглубоко скопированный) массив sortedArray и примените метод .sort() к этому новому массиву.

Проверьте код ниже:

function App() {

  const { loading, error, data } = useQuery(GET_AMMO);

  if (loading) return 'Loading';
  if (error) return 'Error';

  console.info(data.ammo);

  // Spread ammo data to a new shallow-copied array and use sort() function on the new array
  const sortedArray = [...data.ammo].sort((a, b) => a.damage - b.damage);

  return (
    <Grid templateColumns='repeat(5, 1fr)' gap = {6}>
      {sortedArray
      .map((ammos) => (
        <AmmoBox name = {ammos.item.name} caliber = {ammos.caliber} penetration = {ammos.penetrationPower} damage = {ammos.damage}/>))}
    </Grid>
  );
}

Документы Mozilla - Array.prototype.sort() :

Метод sort() возвращает ссылку на исходный массив, поэтому изменение возвращаемого массива также приведет к изменению исходного массива.

Если вы хотите, чтобы sort() не изменяла исходный массив, а возвращала неглубоко скопированный массив, как это делают другие методы массива (например, map()), вы можете сделать поверхностную копию перед вызовом sort(), используя расширение синтаксис или Array.from().

Спасибо! В какой-то момент я попытался скопировать отсортированные результаты в новую переменную, но забыл написать data.ammo как [...data.ammo]. Очень признателен

nickbeaumont 16.12.2022 00:58

Другие вопросы по теме