Home > Mobile >  Why react component does not render after state was changed
Why react component does not render after state was changed

Time:05-05

Better to show you my code, it's compressed so not gonna take much time

const PropertiesList: FC = (estatesObject) => {
    const [estates, setEstates] = useState(Object.values(estatesObject))

    const filterEstatesUp = () => {
        // just sorting an array of objects
        const filteredEstates = estates.sort((a, b) => parseFloat(a.price) - parseFloat(b.price))
        setEstates(filteredEstates)
    }
    const filterEstatesDown = () => {
        // just sorting an array of objects
        const filteredEstates = estates.sort((a, b) => parseFloat(b.price) - parseFloat(a.price))
        setEstates(filteredEstates) 
    }

    return (
        <> 
            <div onClick={() => filterEstatesUp()}>up</div>
            <div onClick={() => filterEstatesDown()}>down</div>
            {estates.map((estate, index) => {
                return(
                    <PropertyCard key={index} {...estate}/>
                )
            })}
        </>
    )
}

So problem is when i'm clicking on these divs up n down - nothing changes

Update x: (I'm using next.js if that will help with something)I changed my code with yours recommendations but i got something else: now state is changing but browser still somehow does not show me another order of estates, so i see cards of estates staying in old order This is my changed code:

const PropertiesList: FC = (estatesObject) => {
    const [estates, setEstates] = useState(Object.values(estatesObject))

    const sortEstatesFromDown = () => {
        const sortedEstates = [...estates]
        sortedEstates.sort((a, b) => parseFloat(a.price) - parseFloat(b.price))
        console.log("FROM DOWN TO UP", sortedEstates)
        setEstates(sortedEstates)
    }

    const sortEstatesFromUp = () => {
        const sortedEstates = [...estates]
        sortedEstates.sort((a, b) => parseFloat(b.price) - parseFloat(a.price))
        console.log("FROM UP TO DOWN", sortedEstates)
        setEstates(sortedEstates)
    }

    return (
        <> 
            <div onClick={() => sortEstatesFromDown()}>up</div>
            <div onClick={() => sortEstatesFromUp()}>down</div>
            {estates.map((estate) => {
                return(
                    <PropertyCard key={estate.id} {...estate}/>
                )
            })}
        </>
    )
}

MAX price is 3.000.000 and MIN is 180.000, but as you can see after clicking both in turn - nothing changed again, there is usual order, that comes from server enter image description here

Update x 1: This state is successfully changing(you can see it from console), but browser does not change order of cards, why?

CodePudding user response:

The sort function does not create a new array, it mutates the old one. So you're rearranging the existing state, and then setting state with the same array. Since it's the same array, react thinks the state hasn't changed and skips rendering.

const sortedEstates = [...estates];
sortedEstates.sort(...);

setEstates(sortedEstates);

CodePudding user response:

Now your code is just mutating your estates array and then passing the same array to the setEstates function so nothing changes because you pass the same reference of an array to that function and React thinks the state should not be changed so it doesn't re-render the component.

The solution looks like this:

const filterEstatesUp = () => {     
    setEstates((prevEstates) => {         
        const copiedEstates = [...prevEstates];         
        return copiedEstates.sort((a, b) => parseFloat(a.price) - parseFloat(b.price)); 
    }) 
}

In this case, I used the setState callback approach because I am changing the state that depends on the previous state, and I also recommend you to use it in order to avoid redundant bugs.

Then in the callback function, first I copy the previous array of estates which means that I am creating a completely new array with the same elements and after that, I sort and return the copied array.

According to the update, I will suggest you use estate.id as a key instead of index, in the place where you map all estates, I think the issue is hiding behind that:

return (
    <> 
        <div onClick={() => sortEstatesFromDown()}>up</div>
        <div onClick={() => sortEstatesFromUp()}>down</div>
        {estates.map((estate, index) => {
            return(
                <PropertyCard key={estate.id} {...estate}/>
            )
        })}
    </>
)

CodePudding user response:

as Feliz Kling said you are not changing your array, and react doesnt rerender. you can use spread operator to do that:

setEstates([...filteredEstates])

a simple example codesandbox

Update: when indexes are used as keys reordering a list, or adding and removing items from a list can cause issues with the component state. If the key is an index, reordering an item changes it. Hence, the component state can get mixed up and may use the old key for a different component instance.

you can change your key to key={estate.id}

  • Related