„Wenn ein Arbeiter seine Arbeit gut machen will, muss er zuerst seine Werkzeuge schärfen.“ – Konfuzius, „Die Gespräche des Konfuzius. Lu Linggong“
Titelseite > Programmierung > Reaktion: veralteter Abschluss

Reaktion: veralteter Abschluss

Veröffentlicht am 02.09.2024
Durchsuche:171

In diesem Beitrag zeige ich, wie man einen Abschluss in einer useState-Hook-React-App erstellt.

Ich werde nicht erklären, was ein Abschluss ist, da es viele Ressourcen zu diesem Thema gibt und ich mich nicht wiederholen möchte. Ich empfehle die Lektüre dieses Artikels von @imranabdulmalik.

Kurz gesagt ist ein Abschluss (von Mozilla):

...die Kombination einer gebündelten (eingeschlossenen) Funktion mit Verweisen auf ihren umgebenden Zustand (die lexikalische Umgebung). Mit anderen Worten, ein Abschluss ermöglicht Ihnen den Zugriff auf den Umfang einer äußeren Funktion von einer inneren Funktion aus. In JavaScript werden Abschlüsse jedes Mal erstellt, wenn eine Funktion zum Zeitpunkt der Funktionserstellung erstellt wird.

Falls Sie mit dem Begriff „lexikalische Umgebung nicht vertraut sind, können Sie diesen Artikel von @soumyadey oder alternativ diesen hier lesen.

Das Problem

In einer React-Anwendung können Sie versehentlich einen Abschluss einer Variablen erstellen, die zum Komponentenstatus gehört, der mit dem useState-Hook erstellt wurde. Wenn dies geschieht, stehen Sie vor einem Problem des veralteten Abschlusses, das heißt, wenn Sie auf einen alten Wert des Zustands verweisen, der sich inzwischen geändert hat und daher nicht relevanter ist.

POC

Ich habe eine Demo-React-Anwendung erstellt, deren Hauptziel darin besteht, einen Zähler (der zum Status gehört) zu erhöhen, der in einem Abschluss im Rückruf der setTimeout-Methode geschlossen werden kann.

Kurz gesagt, diese App kann:

  • Zeige den Wert des Zählers
  • Zähler um 1 erhöhen
  • Starten Sie einen Timer, um den Zähler nach fünf Sekunden um 1 zu erhöhen.
  • Zähler um 10 erhöhen

Im folgenden Bild wird der anfängliche UI-Status der App angezeigt, mit Zähler auf Null.

React: stale closure

Wir simulieren das Schließen des Zählers in drei Schritten:

  1. Zähler um 1 erhöhen

React: stale closure

  1. Starten des Timers, um ihn nach fünf Sekunden um 1 zu erhöhen

React: stale closure

  • Erhöhung um 10, bevor die Zeitüberschreitung ausgelöst wird React: stale closure

Nach 5 Sekunden beträgt der Wert des Zählers 2.

React: stale closure

Der erwartete Wert des Zählers sollte 12 sein, aber wir erhalten 2.

Der Grund, warum dies geschieht, liegt darin, dass wir im an setTimeout übergebenen Rückruf einen Abschluss des Zählers erstellt haben und den Zähler bei Auslösung des Timeouts beginnend mit dessen Wert festlegen alter Wert (das war 1).

setTimeout(() => {
        setLogs((l) => [...l, `You closed counter with value: ${counter}\n and now I'll increment by one. Check the state`])
        setTimeoutInProgress(false)
        setStartTimeout(false)
        setCounter(counter   1)
        setLogs((l) => [...l, `Did you create a closure of counter?`])

      }, timeOutInSeconds * 1000);

Es folgt der vollständige Code der App-Komponente.

function App() {
  const [counter, setCounter] = useState(0)
  const timeOutInSeconds: number = 5
  const [startTimeout, setStartTimeout] = useState(false)
  const [timeoutInProgress, setTimeoutInProgress] = useState(false)
  const [logs, setLogs] = useState>([])

  useEffect(() => {
    if (startTimeout && !timeoutInProgress) {
      setTimeoutInProgress(true)
      setLogs((l) => [...l, `Timeout scheduled in ${timeOutInSeconds} seconds`])
      setTimeout(() => {
        setLogs((l) => [...l, `You closed counter with value: ${counter}\n and now I'll increment by one. Check the state`])
        setTimeoutInProgress(false)
        setStartTimeout(false)
        setCounter(counter   1)
        setLogs((l) => [...l, `Did you create a closure of counter?`])

      }, timeOutInSeconds * 1000);
    }
  }, [counter, startTimeout, timeoutInProgress])

  function renderLogs(): React.ReactNode {
    const listItems = logs.map((log, index) =>
      
  • {log}
  • ); return
      {listItems}
    ; } function updateCounter(value: number) { setCounter(value) setLogs([...logs, `The value of counter is now ${value}`]) } function reset() { setCounter(0) setLogs(["reset done!"]) } return (

    Closure demo


    Counter value: {counter}


    Follow the istructions to create a closure of the state variable counter

    1. Set the counter to preferred value
    2. Start a timeout and wait for {timeOutInSeconds} to increment the counter (current value is {counter})
    3. Increment by 10 the counter before the timeout

    { renderLogs() }
    ); } export default App;

    Lösung

    Die Lösung basiert auf der Verwendung des useRef-Hooks, mit dem Sie auf einen Wert verweisen können, der für das Rendern nicht benötigt wird.

    Also fügen wir der App-Komponente Folgendes hinzu:

    const currentCounter = useRef(counter)
    

    Dann ändern wir den Rückruf von setTimeout wie unten gezeigt:

    setTimeout(() => {
            setLogs((l) => [...l, `You closed counter with value: ${currentCounter.current}\n and now I'll increment by one. Check the state`])
            setTimeoutInProgress(false)
            setStartTimeout(false)
            setCounter(currentCounter.current   1)
            setLogs((l) => [...l, `Did you create a closure of counter?`])
    
          }, timeOutInSeconds * 1000);
    

    Unser Rückruf muss den Zählerwert lesen, da wir den aktuellen Wert vorher protokollieren, um ihn zu erhöhen.

    Falls Sie den Wert nicht lesen müssen, können Sie das Schließen des Zählers vermeiden, indem Sie einfach die funktionale Notation verwenden, um den Zähler zu aktualisieren.

    seCounter(c => c   1)
    

    Ressourcen

    • Dmitri Pavlutin Achten Sie bei der Verwendung von React Hooks auf veraltete Verschlüsse
    • Imran Abdulmalik beherrscht Schließungen in JavaScript: Ein umfassender Leitfaden
    • Keyur Paralkar Lexikalischer Geltungsbereich in JavaScript – Einsteigerhandbuch
    • Souvik Paul Stale Schließungen in React
    • Soumya Dey Verständnis des lexikalischen Geltungsbereichs und der Abschlüsse in JavaScript
    • Subash Mahapatra-Stackoverflow
    Freigabeerklärung Dieser Artikel ist abgedruckt unter: https://dev.to/animusna/react-stale-closure-81a?1 Bei Verstößen wenden Sie sich bitte an [email protected], um ihn zu löschen
    Neuestes Tutorial Mehr>

    Haftungsausschluss: Alle bereitgestellten Ressourcen stammen teilweise aus dem Internet. Wenn eine Verletzung Ihres Urheberrechts oder anderer Rechte und Interessen vorliegt, erläutern Sie bitte die detaillierten Gründe und legen Sie einen Nachweis des Urheberrechts oder Ihrer Rechte und Interessen vor und senden Sie ihn dann an die E-Mail-Adresse: [email protected] Wir werden die Angelegenheit so schnell wie möglich für Sie erledigen.

    Copyright© 2022 湘ICP备2022001581号-3