React : useEffect updates the state, but does not re-render the view

Stack Overflow Asked by Baudoin Michael on August 20, 2020

I’d like to sort through an array of objects.
But I’m stuck here for a while: when the props filterActivated changes, the state is updated (verified in the browser’s react dev tool), but there is no re-rendering.

I don’t understand why the view is not re-rendered, even when the ideasArray is getting updated.

This is my component :

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

// Components
import Idea from './Idea'

const IdeasContainer = ({ filterActivated }) => {

    const [ideasArray, setIdeasArray] = useState([]);

    useEffect(() => {
        const getIdeas = async () => {
            try {
                const response = await fetch("http://localhost:3004/api/ideas")
                const data = await response.json()
            } catch (err) {
                console.error('getIdeas error: ', err, err.stack)
    }, []);

    useEffect(() => {
        const sorted = (array) => {
            let newArray = array.sort(function (a, b) {
                if (filterActivated === 'scoreAsc') {
                    return a.score - b.score
                else if (filterActivated === 'scoreDesc') {
                    return b.score - a.score
    }, [filterActivated, ideasArray]);

    return (
        <div className="ideasContainer">
      , index) => {
                    return <Idea key={index} dataIdea={idea} />

export default IdeasContainer;

One Answer

Firstly, your useEffect has a few issues:

Don't reference the ideasArray as a useEffect dependency if you're setting that value inside the useEffect. That will cause infinite loops because when you set the value, the `useEffect gets called again, which sets the value, which calls it again, which sets the value... and so on. If you depend on the previous value to set the new value, pass a setter function to the setIdeasArray function, for example:

useEffect(() => {
  setIdeasArray(oldValue =>
    [...oldValue].sort((a, b) => {
      if (filterActivated === "scoreAsc") {
        return a.score - b.score;
      } else if (filterActivated === "scoreDesc") {
        return b.score - a.score;
}, [filterActivated]);

Notice how I'm also spreading the oldValue into a new array before sorting. This is because Array.sort is not reliably immutable, so in certain situations it will mutate the original data, which we do not want in React.

If state changes, there is always a re-render. React's render cycle is not the same as the DOM changing, but React will still run the component render cycle if props or state changes.

The issue you're having is due to using the index as a key for each idea. Using indexes as keys is a really bad idea.

The whole point of keys is to allow React to identify items in an array without it having to re-render them, so by using the indexes, what you're essentially saying is that the first item (always index 0) never changes. Yes, your state is re-ordered, but React sees that the keys have not changed order, so it does not change the DOM to match.

Keys need to be related to the item (such as a unique id), so that React can identify that item by something unrelated to "its position within an array".

Correct answer by JMadelaine on August 20, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP