1

This is part of a larger component but I've summarised it in a reproducible example below:

    import React, {useState} from 'react'

   
    function App() {
        const [data,setData] = useState([{name:'first'},{name:'second'},{name:'three'}])

       // Function to reverse a given array
        const reverseArray = (array) => {
          var reverseArray = array.reverse();
          return reverseArray;
        }


        return (
            <div>
                  {data.map((item,index)=>{
                         return <h1 key={index}>{item.name} index: {index}</h1>
                    })}
            

                    {/*When I click this I expect the div above to rerender */}
                    <button onClick={()=>{
                        var newData = data;
                        setData(reverseArray(newData))}
                    }>
                      Reverse Order
                     </button>

                    {/* Logs state to the console to check order is reversed */}
                    <button onClick={()=>console.log(data)}>Log Data</button>

                    {/* Clearing the state by setting to empty array */}
                    <button onClick={()=>setData([ ])}>Clear Data</button>

                    
            </div>
        )
    }

So my main issue, is that the mapped data doesn't seem to change with a state update. As a check that state does update, I have a clear button which does clear the data state and the list goes blank. 

When I click the reverse button, I expect the data to be reversed (i.e. "first" to be last and "three" to be in first place). I'm sure my state is updated as I can check it with console logs before and after the reverse button is clicked.

My thought process was to create a completely new reversed array (newData) and set this as the state. However, the mapping doesn't reflect this order change. What am I missing here? 

I understand components in React will rerender when state changes, why does setting a new array to state not trigger this? But the rerender is apparent when clearing the state?

My sandbox for this: https://codesandbox.io/s/immutable-sun-oujqj?file=/src/App.js

2 Answers 2

4

Demo: https://codesandbox.io/s/array-reverse-react-state-0v67h

Array.prototype.reverse() reverses the elements in place. You'll need to pass a new array into setData().

<button
  onClick={() => {
    var newData = data;

    // Create a new array using the spread operator
    setData(reverseArray([...newData])); 
  }}
>
Sign up to request clarification or add additional context in comments.

2 Comments

Perfect! So does declaring var newData not actually make a copy of the array, but rather reverses the elements in place? Even though it is a separate variable?
@yahms23 newData does not make a copy, it simply adds a handle that points to the exact same array (newData is essentially an alias for data). See: Copy array by value
0

The reason React is not re-rendering the component is because Array.prototype.reverse mutates the array and return the reference

For Example

    const array = [1,2,3,4,5]
    const revArray = array.reverse() // This will reverse 'array' variable and returns it's reference
    
    array === revArray // this statement is true
    console.log(array) // result [5,4,3,2,1]
    console.log(revArray) // result [5,4,3,2,1]

In the above array === revArray because both are referring to the same array, Since that is the case, The state is not actually changing because the referrence to the array is same.

Solution: Simply return a new Array!

 const reverseArray = (array) => {
      const revArray = array.reverse();
      return [...revArray] 
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.