简体   繁体   中英

Why react component does not render after state was changed

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 在此处输入图像描述

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

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

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}/>
            )
        })}
    </>
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM