React, console.log e render exibem valores diferentes
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
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
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
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
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! ;)