0

I have this code where I've updated an object in React. When you click "Most upvoted", it takes an object, sorts it by votes, and then updates the state with it. The problem is, it won't pass down to the Child Component and re-render to update the UI. I'm so close to figuring this out. What can I try next?

Here's my code...

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

const title = "Sorting Articles";

function App({articles}) {

    const [newArticles, setNewArticles] = useState(articles);

    function sortVotes() {
        const sorted = newArticles.sort((a,b) => a.upvotes - b.upvotes);
        setNewArticles(sorted);
    }

    return (
        <div className="App">
            <h8k-navbar header={title}></h8k-navbar>
            <div>
                <label>Sort By</label>
                <button onClick={sortVotes} className="small">Most Upvoted</button>
            </div>
            <Articles articles={newArticles}/>
        </div>
    );

}

export default App;

and here is the code for Articles.js

import React from 'react';

function Articles(props) {
    return (
        <div>
            <table>
                <thead>
                <tr>
                    <th>Title</th>
                    <th>Upvotes</th>
                    <th>Date</th>
                </tr>
                </thead>
                <tbody>
                    {
                        props.articles.map((article) => {
return <tr data-testid="article" key="article-index">
                    <td>{article.title}</td>
                    <td>{article.upvotes}</td>
                    <td>{article.date}</td>
                </tr>
                        })
                    }
                
                </tbody>
            </table>
        </div>
    );

}

export default Articles;

Here is the code for App.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);
})
3
  • Where is the articles passed as props to the App component and set as the initial state for the newArticles defined? Commented Apr 23 at 21:03
  • It's defined in the App.js file. I've updated the code to include App.js. Commented Apr 23 at 21:11
  • 1
    This question is similar to: Not able to sort an array of objects initiated using the useState hook. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem. Commented Apr 24 at 7:27

2 Answers 2

2

Your main problem is the sort function here. See, sort is an in-place operation, and object types in javascript are stored as references. So in your code you:

  1. Use useState to create newArticles (newArticles is a reference)
  2. set a new const sorted as newArticles.sort(...), this is the same reference that points at newArticles since sort is in-place.
  3. Use setNewArticles on sorted which is just the same reference with newArticles. React sees the same value and does not see a reason to re-render.

One option to fix this is to copy the object first send set newArticles to this newly created object like this:

function sortVotes() {
  const sorted = [...newArticles].sort((a, b) => a.upvotes - b.upvotes);
  setNewArticles(sorted);
}

Important note: trying to mutate state variables directly is a bad practice and can cause bugs. Please avoid that.

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

Comments

-1

Inside the function sortVotes, I set the mutated object to a new variable called sorted and then passed that variable into the setNewArticles function.

Here is the code

const sorted = newArticles.sort((a,b) => a.upvotes - b.upvotes);
setNewArticles(sorted);

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.