0

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...

1
  • 1
    Sorting works in-place, so state is being directly modified--that can lead to unexpected results. Commented Nov 18, 2021 at 17:21

2 Answers 2

1

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

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks man great help
1

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]);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.