Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

181
Visualizações
Why does calling one `setState` function update an entirely separate state value?

I have an issue where one setState function is updating two separate state values.

I am trying to make a small React sortable array. The program should behave like so:

  • fetches data on mount
  • stores that data in both unsortedData & displayedData state hooks
  • when a user clicks "toggle sorting" button the array in displayedData is sorted (using .sort())
  • on a second click of "toggle sorting" it should set displayedData to be the same value as unsortedData - restoring the original order

However, on first click of toggle sorting, it sorts both unsortedData & displayedData, which means I lose the original data order. I understand I could store their order, but I'm wanting to know why these state values appear to be coupled.

Stackblitz working code here.
GIF showing issue here

I cannot find where this is happening. I can't see any object/array referencing going on (I'm spreading for new objects/arrays).

Code here:

const Test = () => {
  const [unsortedData, setUnsortedData] = useState([])
  const [displayedData, setDisplayedData] = useState([])
  const [isSorted, setisSorted] = useState(false)

  const handleSorting = () => setisSorted(!isSorted)

  useEffect(() => {
    if (isSorted === true) setDisplayedData([...unsortedData.sort()]) // sort data
    if (isSorted === false) setDisplayedData([...unsortedData]) // restore original data order
  }, [isSorted, unsortedData])

  useEffect(() => {
    const mockData = [3, 9, 6]
    setUnsortedData([...mockData]) // store original data order in "unsortedData"
    setDisplayedData([...mockData])
  }, [])

  return (
    <div>
      {displayedData.map(item => item)}
      <br />
      <button onClick={() => handleSorting()}>Toggle sorting</button>
    </div>
  )
}
about 4 years ago · Juan Pablo Isaza
2 Respostas
Responde à pergunta

0

The .sort function mutates the array, so when you do this:

setDisplayedData([...unsortedData.sort()])

You are mutating unsortedData, then making a copy of it afterwards. Since you mutated the original array, that change can display on the screen when the component rerenders.

So a minimum fix would be to copy first, and sort afterwards:

setDisplayedData([...unsortedData].sort())

Also, why do I need the useEffect? Why can't I just move the contents of the useEffect (that has the if statements in) into the handleSorting function?

Moving it into handleSorting should be possible, but actually i'd like to propose another option: have only two states, the unsorted data, and the boolean of whether to sort it. The displayed data is then a calculated value based on those two.

const [unsortedData, setUnsortedData] = useState([3, 9, 6]) // initialize with mock data
const [isSorted, setisSorted] = useState(false)

const displayedData = useMemo(() => {
  if (isSorted) {
    return [...unsortedData].sort();
  } else {
    return unsortedData
  }
}, [unsortedData, isSorted]);

const handleSorting = () => setisSorted(!isSorted)

// No use useEffect to sort the data
// Also no useEffect for the mock data, since i did that when initializing the state

The benefits of this approach are that

  1. You don't need to do a double render. The original version sets isSorted, renders, then sets displayed data, and renders again
  2. It's impossible to have mismatched states. For example, in between that first and second render, isSorted is true, and yet the displayed data isn't actually sorted yet. But even without the double render case, having multiple states requires you to be vigilant that every time you update unsortedData or isSorted, you also remember to update displayedData. That just happens automatically if it's a calculated value instead of an independent state.
about 4 years ago · Juan Pablo Isaza Relatório

0

If you want to display sorted array to user only when the user presses the button, you could use this code statement (orignaly coppied from vue3 todo list)

const filters = {
  none: (data) => [...data],
  sorted: (data) => [...data].sort((a, b) => a - b),
}

const Test = () => {
  const [unsortedData, setUnsortedData] = useState([])
  const [currentFilter, setCurrentFilter] = useState('none')

  useEffect(() => {
    const mockData = [3, 9, 6]
    setUnsortedData([...mockData])
  }, [])

  return (
    <div>
      <ul>
         {/* All magic is going here! */}
         {filters[currentFilter](unsortedData).map(item => <li key={item}>{item}</li>)}
      </ul>
      <br />
      <button
        onClick={() =>
            setCurrentFilter(currentFilter === 'none' ? 'sorted' : 'none')}>
          Toggle sorting
       </button>
    </div>
  )
}
about 4 years ago · Juan Pablo Isaza Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda