Home > Software design >  Why is react sorting the opposite thing?
Why is react sorting the opposite thing?

Time:09-26

I am working on this app which is supposed to sort everything by date or upvotes in descending order according to which of the 2 buttons is pressed. It works except it sorts the opposite thing e.g when I click upvotes and sortType === "upvotes" then it sorts by date and when I click date and sortType === "date" it sorts by upvotes. I have no idea why but I can't figure out why and I really want to know what is wrong.

Thank you very much in advance

App.js

import React, { useState } from 'react';
import './App.css';

import Articles from './components/Articles';

const title = "Sorting Articles";

function App({articles}) {

    const [ sortType , setSortType ] = useState("upvotes");

    return (
        <div className="App">
            <h8k-navbar header={title}></h8k-navbar>
            <div className="layout-row align-items-center justify-content-center my-20 navigation">
                <label className="form-hint mb-0 text-uppercase font-weight-light">Sort By</label>
                <button data-testid="most-upvoted-link" className="small" onClick={()=>setSortType("upvotes")}>Most Upvoted</button>
                <button data-testid="most-recent-link" className="small" onClick={()=>setSortType("date")}>Most Recent</button>
            </div>
            <Articles articles={articles} sortType={sortType}/>
        </div>
    );

}

export default App;

Articles.js

import React, { useEffect, useState } from 'react';

function Articles({articles, sortType}) {
    
    const [ myArticles , setMyArticles] = useState([]);

    useEffect(()=>{
        if (sortType === "date") articles = articles.sort((a, b) => (b.date > a.date) ? 1 : -1);
        else articles = articles.sort((a, b) => (b.upvotes > a.upvotes) ? 1 : -1);
        setMyArticles(articles);
        console.log(sortType); //showing correctly but sorting opposite thing
    },[sortType])

    return (
        <div className="card w-50 mx-auto">
            <table>
                <thead>
                <tr>
                    <th>Title</th>
                    <th>Upvotes</th>
                    <th>Date</th>
                </tr>
                </thead>
                <tbody>
                {myArticles.map((myArticle,i)=>
                <tr data-testid="article" key={i}>
                    <td data-testid="article-title">{myArticle.title}</td>
                    <td data-testid="article-upvotes">{myArticle.upvotes}</td>
                    <td data-testid="article-date">{myArticle.date}</td>
                </tr>)}
                </tbody>
            </table>
        </div>
    );

}

export default Articles;

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import {applyPolyfills, defineCustomElements} from 'h8k-components/loader';

const ARTICLES = [
  {
    title: "A message to our customers",
    upvotes: 12,
    date: "2020-01-24",
  },
  {
    title: "Alphabet earnings",
    upvotes: 22,
    date: "2019-11-23",
  },
  {
    title: "Artificial Mountains",
    upvotes: 2,
    date: "2019-11-22",
  },
  {
    title: "Scaling to 100k Users",
    upvotes: 72,
    date: "2019-01-21",
  },
  {
    title: "the Emu War",
    upvotes: 24,
    date: "2019-10-21",
  },
  {
    title: "What's SAP",
    upvotes: 1,
    date: "2019-11-21",
  },
  {
    title: "Simple text editor has 15k monthly users",
    upvotes: 7,
    date: "2010-12-31",
  },
];

ReactDOM.render(<App articles={ARTICLES} />, document.getElementById('root'));
registerServiceWorker();

applyPolyfills().then(() => {
    defineCustomElements(window);
})

CodePudding user response:

The sort function doesn't return a new array, so I think the problem here is React doesn't detect a state change until you change the sort type more than once. Plus, what you're doing when writing articles = articles.sort(...) is modifying a state variable directly, which not only doesn't work but is also a bad practice.

Change your effect to the following:

 useEffect(()=>{
        let sortedArticles = [...articles]
        if (sortType === "date") sortedArticles = sortedArticles.sort((a, b) => (b.date > a.date) ? 1 : -1);
        else sortedArticles = sortedArticles.sort((a, b) => (b.upvotes > a.upvotes) ? 1 : -1);
        setMyArticles(sortedArticles);
    },[sortType])

What you're essentially doing here is copying the existing articles array into a new array, sorting the new array and then setting the state to it. Remember, React detects changes in an array only if you're providing it with an entirely new arary

CodePudding user response:

Disclaimer: I didn't use React hooks before. Based on the documentation to me it seems like useEffect is running after the render and therefore it's not the function/lifecycle event you want to use. https://reactjs.org/docs/hooks-reference.html#useeffect One solution you could try is refactoring your list to the parent where the button is and passing down the sorted list. The other unrelated thing is to consider creating a sortedArray/filteredArray variable as the target of any modification you want to do on your list and keeping the original intact. With sorting it's not necessary but if you want to apply filters too in the future this might come handy.

  • Related