1

My issue similar to this, but not the same.

I'm trying to sort an array of objects using localeCompare. It has no reason not to work, but it's not working. Somehow I'm doing something right./sigh/

This is how I fetch the data

  const [data, setData] = useState([]);
  const [isBusy, setBusy] = useState(true)

  useEffect(() => {
    console.log('I was run once');
    async function fetchData() {
      const url = `${
        process.env.REACT_APP_API_BASE
        }/api/v1/endpoint/`;

      axios.get(url).then((response: any) => {
        setBusy(false);
        setData(response.data.results)
        console.log(response.data.results);
      });
    }

    fetchData();
  }, [])

This is the sorting function

  function onSort(event: any, sortKey: string) {
    const newData = data;
    newData.sort((a: any, b: any): any => {
      a[sortKey].toString().localeCompare(b[sortKey]);
    })
    setData(newData);
  }

A typical array of objects is like this

[
  {
    "avatar": "big",
    "name": "Username",
  },
  {
    "avatar": "small",
    "name": "Rex",
  },
  {
    "avatar": "medium",
    "name": "Duh",
  }
]

Then the table header has this

<th scope="col" className="text-center" onClick={e => onSort(e, 'name')}>Name</th>

Although the onSort() function runs without any errors, the objects don't get sorted as per the sortKey

Am I using the localeCompare wrongly? What can I do to make the above right?

Also, the setData, I believe should update the data and thus trigger a rebuild of the table, which seems to be happening, because if I pass in an empty array to the setData, the table blanks.

edit:

The HTML part

              ...
              return (
                <div>
                  <table className="table table-hover">
                    <thead>
                      <tr>
                        <th onClick={e => onSort(e, 'avatar')}>Avatar</th>
                        <th onClick={e => onSort(e, 'name')}>Name</th>
                    </thead>
                    <tbody>
                      {data && data.map((item: any, index: any) => {
                        return (
                          <tr key={index}>
                            <th>{{ item.avatar}}</th>
                            <td>{item.name}</td>
                            <td className="text-center" style={{ 'fontSize': '32px', 'color': '#981c1e' }}>0</td>
                          </tr>
                        )
                      })}

                    </tbody>
                  </table>
                 </div>
              ...

If I understand, if the setData triggers a data change, the render re-renders?

KhoPhi
  • 9,660
  • 17
  • 77
  • 128
  • 2
    See the [linked question's answers](https://stackoverflow.com/questions/45754957/why-doesnt-my-arrow-function-return-a-value), your arrow function has no return value. Either `newData.sort((a: any, b: any): any => { return a[sortKey].toString().localeCompare(b[sortKey]); })` (added `return`) or `newData.sort((a: any, b: any): any => a[sortKey].toString().localeCompare(b[sortKey]))` (no `{}`). – T.J. Crowder Nov 22 '19 at 15:51
  • 1
    You can't set state with a mutated version because React [won't render](https://codesandbox.io/s/mutate-state-bad-t264j) You should do: `const newData = [...data];` – HMR Nov 22 '19 at 17:34
  • @HMR You're right. That's the solution. – KhoPhi Nov 23 '19 at 21:30

1 Answers1

5

You need a return statement by using curly brackets in an arrow function

newData.sort((a: any, b: any): any => {
    return a[sortKey].toString().localeCompare(b[sortKey]);
});
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392