Search code examples
javascriptreactjsgraphqlapollo

How can I sort an array of objects from an API before passing them into a react component?


Hello I'm fairly new to react/js and I am trying to sort some data that I am fetching from the an api using Apollo then render the data into a grid. When I try to use the sort() function I get and error in the console "TypeError: Attempting to change configurable attribute of unconfigurable property.". Can anyone help explain what I'm doing wrong? Many thanks

This is the code I've tried. It works without the data.ammo.sort method.

I was expecting the data to be rendered in order sorted by damage

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.log(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;

Solution

  • Since the error says you can't change data.ammo, spread the data to a new (shallow-copied) array sortedArray and apply .sort() method to this new array.

    Check code below:

    function App() {
    
      const { loading, error, data } = useQuery(GET_AMMO);
    
      if (loading) return 'Loading';
      if (error) return 'Error';
    
      console.log(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 Docs - Array.prototype.sort() :

    The sort() method returns a reference to the original array, so mutating the returned array will mutate the original array as well.

    In case you want sort() to not mutate the original array, but return a shallow-copied array like other array methods (e.g. map()) do, you can do a shallow copy before calling sort(), using the spread syntax or Array.from().