Home > Software design >  Sorted items of array not rendering properly with react
Sorted items of array not rendering properly with react

Time:11-19

I'm using react and this is my code.

import { useEffect, useState } from "react";

const Test = (props) => {

    const [sort, setSort] = useState(1);
    const [albums, setAlbums] = useState([
        {
            "userId": 1,
            "id": 1,
            "title": "A letter",
            "photos": {
                "albumId": 1,
                "id": 1,
                "title": "accusamus beatae ad facilis cum similique qui sunt",
                "url": "https://via.placeholder.com/600/92c952",
                "thumbnailUrl": "https://via.placeholder.com/150/92c952"
            },
        },
        {
            "userId": 1,
            "id": 2,
            "title": "C letter",
            "photos": {
                "albumId": 2,
                "id": 1,
                "title": "accusamus beatae ad facilis cum similique qui sunt",
                "url": "https://via.placeholder.com/600/92c952",
                "thumbnailUrl": "https://via.placeholder.com/150/92c952"
            },
        },
        {
            "userId": 1,
            "id": 3,
            "title": "B letter",
            "photos": {
                "albumId": 3,
                "id": 1,
                "title": "accusamus beatae ad facilis cum similique qui sunt",
                "url": "https://via.placeholder.com/600/92c952",
                "thumbnailUrl": "https://via.placeholder.com/150/92c952"
            },
        },
    ]);

    useEffect(() => {
        if (sort == 1) {
            setAlbums(albums.sort((a, b) => (a.id > b.id) ? 1 : -1));
        } else if (sort == 2) {
            setAlbums(albums.sort((a, b) => (a.title > b.title) ? 1 : -1));
        } else if (sort == 3) {
            setAlbums(albums.sort((a, b) => (a.title < b.title) ? 1 : -1));
        }
    }, [sort]);

    return (
        <>
            <select value={sort} onChange={e => setSort(e.target.value)} >
                <option value='1'>val 1</option>
                <option value='2'>val 2</option>
                <option value='3'>val 3</option>
            </select>
            {albums.map((item, key) => {
                return (
                    <p>{item.title}</p>
                )
            })}
        </>
    )
}

export default Test;

I'm using react and trying to sort array items with select dropdown. But when I sort it's always one level behind. That means if I select 2 nothing would happen and then select 3 it will get sorted according to value 2. It would be great if someone can give a solution and explain why this happens...

CodePudding user response:

this looks like a good use case for the useMemo hook. try something like this:

const sortedAlbums = useMemo(() => {
    if (sort === 1) return albums.sort((a, b) => (a.id > b.id) ? 1 : -1);
    ...
}, [albums, sort]);

official docs: https://reactjs.org/docs/hooks-reference.html#usememo

CodePudding user response:

Array.prototype.sort sorts an array in-place—so you can't use it on state. What you need to do is call sort on a copy of the array, like so.

useEffect(() => {
  // deep copy
  const copy = JSON.parse(JSON.stringify(albums));
  if (sort == 1) {
    setAlbums(copy.sort((a, b) => (a.id > b.id ? 1 : -1)));
  } else if (sort == 2) {
    setAlbums(copy.sort((a, b) => (a.title > b.title ? 1 : -1)));
  } else if (sort == 3) {
    setAlbums(copy.sort((a, b) => (a.title < b.title ? 1 : -1)));
  }
}, [sort]);

Side note: you could refactor that code like so:

useEffect(() => {
  const copy = JSON.parse(JSON.stringify(albums));
  const conditions = [
    (a, b) => (a.id > b.id ? 1 : -1),
    (a, b) => (a.title > b.title ? 1 : -1),
    (a, b) => (a.title < b.title ? 1 : -1),
  ];
  setAlbums(copy.sort(conditions[sort - 1];
}, [sort]);
  • Related