React, console.log e render visualizzano valori diversi

Oct 25 2020

Sembra che l' oggetto registrato sulla console non corrisponda a ciò che viene visualizzato sull'HTML , sono perplesso da questo semplice esempio di seguito. Qui il pulsante alterna l'ordine dell'array e React sembra visualizzare l'elenco precedente.

=== Aggiorna 3 ====

La cuprite è un oggetto compassione in cui setState(list=>list.push(12))muterà la lista ma non attiverà setState perché l'id della lista è sempre lo stesso.

Se aggiorni un gancio di stato allo stesso valore dello stato corrente, React salterà senza rendere i figli o gli effetti di fuoco. (React utilizza l'algoritmo di confronto Object.is.) React useState doc

=== Aggiorna 2 ====

Penso di aver trovato la soluzione. Il colpevole sembra essere la mutabilità degli oggetti e la soluzione alternativa è creare una copia completa dell'elenco ordinato in Solution Sandbox . Credo di aver riscontrato un comportamento indefinito di React, poiché la mutabilità di list non dovrebbe causare differenze console.log(list)e <h1>list</h1>mostrate nell'immagine sottostante. Modifica corretta degli array di stato in ReactJS

=== Aggiorna 1 ====

Sandbox che contiene lo stesso codice di quello mostrato di seguito.

Non c'è niente di sbagliato nella mia funzione di ordinamento! Se ascendente, l'elemento più piccolo dovrebbe essere il primo, il che significa che la funzione di confronto (e1,e2)=>e1-e2è corretta.

Se compareFunction (a, b) restituisce meno di 0, ordina a un indice inferiore a b (cioè a viene prima). Funzione di confronto 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'/>

Risposte

2 arminyahya Nov 03 2020 at 15:55

secondo MDN, la funzione di ordinamento javascript muta l'array.

Il metodo sort () ordina gli elementi di un array sul posto e restituisce l'array ordinato.

quando scrivi:

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

stai mutando la lista prima che setList la muti.

setList controlla se il valore dato è diverso dal precedente quindi renderizza.

ma setList non vede differenze poiché il valore dell'elenco è già aggiornato dal metodo di ordinamento.

per questo motivo, il rendering sembra un passo indietro.

quindi dovresti scrivere:

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

un'altra cosa:

Penso che dovresti cambiare

{isAscending ? "Ascending" : "Descending"}

per

{isAscending ? "Descending" : "Ascending"}

poiché è un pulsante di attivazione / disattivazione

2 DarshnaRekha Nov 02 2020 at 20:34

Il tuo ordine iniziale è in ordine crescente. Quando si fa clic sul pulsante, la prima volta per alternare l'ordine in cui si desidera passare dall'ordine ascendente a quello discendente.

Che 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));
}

Controlla il codice qui e fammi sapere se questo è quello che stavi cercando. Codice sandbox: l'elenco ordinato non viene visualizzato

1 DennisVash Oct 25 2020 at 13:35

Hai una chiusura sul listvalore al useEffectcallback, dovresti aggiungerlo al tuo 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>
  );
}

Nota che se lo eslinthai dovresti ricevere un avviso su di esso.

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

1 LouysPatriceBessette Nov 04 2020 at 01:01

Ho semplificato un po 'il tuo frammento.

Stranamente stavi usando entrambi useStatee useReducermentre il secondo è un'alternativa al primo.

useReducer è generalmente preferibile a useState quando si dispone di una logica di stato complessa che coinvolge più sottovalori o quando lo stato successivo dipende da quello precedente.

Citato dai documenti di 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'/>

Così qui listè l' initialStatedell'esempio documentazione. E la logica per riordinare l'elenco, che ora è molto semplice, è all'interno della funzione di riduzione.

Sperando che aiuti! ;)