Syn
Syn

Reputation: 1128

React not re-rendering when state changed from hook

Edit: Thanks for the help everyone. I needed to change the reference of the array and fixed it by doing:

setData([...sorted])

I am currently rendering out a list of tasks. This is a snippet of my return function within a functional component:

const [ data, setData ] = useState( mockData )

<tbody>
{ data.map(d => <TaskItem key={d.claimable} task={d}/>) }
</tbody>

When I click on a certain button on the page, the dataset gets sorted and I call setData(sortedData)

For some reason, the table isnt being re-rendered with the sorted data. Is there something I did wrong here?

This is the sort function:

function filterByContactAmount():void {
        let sorted = data.sort((a:any, b:any) => {
            let aTimesContacted:number = a.data.person.contact.list.reduce((acc:number, val:any):number => acc + val.history.length, 0)
            let bTimesContacted:number = b.data.person.contact.list.reduce((acc:number, val:any):number => acc + val.history.length, 0) 

            if ( aTimesContacted > bTimesContacted ) {
                return 1
            }

            if ( bTimesContacted > aTimesContacted ) {
                return -1
            }

            return 0;
        })

        console.log(sorted)
        setData(sorted)
    }

Upvotes: 1

Views: 88

Answers (3)

HMR
HMR

Reputation: 39250

You are mutating sate, the other answer is probably not the best because you are still mutating state and then setting state with a copy of the already mutated value.

The sort function can also be optimized. Maybe try the following:

function filterByContactAmount() {
  let sorted = data
    .map(d => ({//map shallow copies the array
      ...d,//shallow copies the item
      sortedNum: d.data.person.contact.list.reduce(//do this once for every item, not for every time sort callback is called
        (acc, val) => acc + val.history.length,
        0
      ),
    }))
    .sort((a, b) => a.sortedNum - b.sortedNum);

  console.log(sorted);
  setData(sorted);
}

Upvotes: 1

Black Hole
Black Hole

Reputation: 1382

Its because you are using the same ref of the array, you need set the new data with

setData(old => "sorted data");

to change the reference of the state and it updates

function filterByContactAmount():void {
        let sorted = data.sort((a:any, b:any) => {
            let aTimesContacted:number = a.data.person.contact.list.reduce((acc:number, val:any):number => acc + val.history.length, 0)
            let bTimesContacted:number = b.data.person.contact.list.reduce((acc:number, val:any):number => acc + val.history.length, 0) 

            if ( aTimesContacted > bTimesContacted ) {
                return 1
            }

            if ( bTimesContacted > aTimesContacted ) {
                return -1
            }

            return 0;
        })

        console.log(sorted)
        setData(old => [...sorted]) // Sorted is the new state sorted
    }

Upvotes: 1

loelsonk
loelsonk

Reputation: 3598

I think issue is located under d.claimable, I suppose it is boolean variable type. You must know that every key prop must be unique. Check if you have for example.id property, if not add it.

Uniqueness of key prop is very important during reconciliation process.

Unique identifier with a group of children to help React figure out which items have changed, have been added or removed from the list. It’s related to the “lists and keys” functionality of React described here.

Very nice article about reconciliation.

<tbody>
{ data.map(d => <TaskItem key={d.claimable} task={d}/>) }
</tbody>

Upvotes: 0

Related Questions