React, console.log e render exibem valores diferentes

Oct 25 2020

Parece que o objeto registrado no console não corresponde ao que é exibido no HTML , estou intrigado com este exemplo simples abaixo. Aqui, o botão alterna a ordem do array e o React parece renderizar a lista anterior.

=== Atualização 3 ====

O cuprite é o objeto compaixão onde setState(list=>list.push(12))irá alterar a lista, mas não irá acionar setState porque o id da lista ainda é o mesmo.

Se você atualizar um State Hook com o mesmo valor que o estado atual, o React irá sair sem renderizar os filhos ou disparar efeitos. (React usa o algoritmo de comparação Object.is.) React useState doc

=== Atualização 2 ====

Acho que encontrei a solução. O culpado parece ser a mutabilidade dos objetos, e a solução alternativa é criar uma cópia profunda da lista classificada no Solution Sandbox . Acredito ter encontrado um comportamento indefinido do React, já que a mutabilidade da lista não deve causar diferenças no console.log(list)e <h1>list</h1>mostrado na imagem abaixo. Modificação correta de matrizes de estado no ReactJS

=== Atualização 1 ====

Sandbox que contém o mesmo código mostrado abaixo.

Não há nada de errado com minha função de classificação! Se for ascendente, o elemento menor deve vir primeiro, o que significa que a função de comparação (e1,e2)=>e1-e2está correta.

Se compareFunction (a, b) retornar menos que 0, classifique a para um índice menor que b (ou seja, a vem primeiro). Função de comparação Mozilla

const {useState, useEffect, useReducer} = React;

function App() {
  const [list, setList] = useState([0,1]);
  const [isAscending, toggle] = useReducer(isAscending=>!isAscending, true);
  // update the order of the list
  useEffect(()=>{
    if (isAscending) {
      setList(list.sort((e1,e2)=>e1-e2));
    } else {
      setList(list.sort((e1,e2)=>e2-e1));
    }
  },[list, isAscending]);
  // render
  return (
    <div>
    {console.log("list[0] should be",list[0])}
    <button onClick={toggle}>{isAscending?"Ascending":"Descending"}</button>
    <h1>list[0]: {list[0]}</h1>
    </div>
  )
}

ReactDOM.render(<App/>, document.querySelector('.App'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div class='App'/>

Respostas

2 arminyahya Nov 03 2020 at 15:55

de acordo com o array mutates da função de classificação javascript MDN.

O método sort () classifica os elementos de uma matriz no local e retorna a matriz classificada.

quando você escreve:

setList(list.sort((e1, e2) => e1 - e2));

você está alterando a lista antes de setList alterá-la.

setList verifica se o valor fornecido é diferente do anterior e então renderiza.

mas setList não vê nenhuma diferença, pois o valor da lista já está atualizado pelo método de classificação.

por causa disso, render parece um passo atrás.

então você deve escrever:

setList([...list].sort((e1, e2) => e1 - e2));

mais uma coisa:

eu acho que você deveria mudar

{isAscending ? "Ascending" : "Descending"}

para

{isAscending ? "Descending" : "Ascending"}

já que é um botão de alternância

2 DarshnaRekha Nov 02 2020 at 20:34

Seu pedido inicial é crescente. Ao clicar no botão, a primeira vez para alternar a ordem que você deseja mover de crescente para decrescente.

Que significa

if (isAscending) {
    // Logic for descending should come here
    setList(list.sort((e1, e2) => e2 - e1));
} else {
    // Logic for ascending should come here
    setList(list.sort((e1, e2) => e1 - e2));
}

Verifique o código aqui e me diga se é isso que você estava procurando. Sandbox de código: a lista classificada não é renderizada

1 DennisVash Oct 25 2020 at 13:35

Você tem um listvalor de fechamento no useEffectretorno de chamada, você deve adicioná-lo ao seu array dep:

function App() {
  const [list, setList] = useState([0, 1]);
  const [isAscending, toggle] = useReducer((isAscending) => !isAscending, true);

  useEffect(() => {
    setList(list.sort((e1, e2) => (!isAscending ? e1 - e2 : e2 - e1)));
  }, [list, isAscending]);

  return (
    <div>
      <button onClick={toggle}>
        {isAscending ? "Ascending" : "Descending"}
      </button>
      <pre>{JSON.stringify(list)}</pre>
    </div>
  );
}

Observe que, se tiver, eslintvocê deve receber um aviso sobre isso.

https://codesandbox.io/s/react-template-forked-08eft?file=/index.js:99-684

1 LouysPatriceBessette Nov 04 2020 at 01:01

Simplifiquei um pouco o seu trecho.

Estranhamente, você estava usando ambos useStatee useReducerenquanto o segundo é uma alternativa para o primeiro.

useReducer geralmente é preferível a useState quando você tem uma lógica de estado complexa que envolve vários subvalores ou quando o próximo estado depende do anterior.

Citado em documentos Reactjs

const {useReducer} = React;

function App() {
  const list=[0,1]
  const [isAscending, toggle] = useReducer(isAscending=>{
  
    if (isAscending) {
      list.sort((e1,e2)=>e2-e1)
    } else {
      list.sort((e1,e2)=>e1-e2)
    }

    return !isAscending
  }, list);

  // render
  return (
    <div>
    {console.log("list[0] should be",list[0])}
    <button onClick={toggle}>{isAscending?"Ascending":"Descending"}</button>
    <h1>list[0]: {list[0]}</h1>
    </div>
  )
}

ReactDOM.render(<App/>, document.querySelector('.App'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div class='App'/>

Então aqui listestá o initialStateexemplo de documentação. E a lógica para reordenar a lista, que agora é bem simples, está dentro da função redutora.

Espero que ajude! ;)