Heim  >  Artikel  >  Web-Frontend  >  Reaktion: veralteter Abschluss

Reaktion: veralteter Abschluss

王林
王林Original
2024-08-21 06:19:02374Durchsuche

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 lesen oder alternativ diesen.

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 mit der altmodischen Schließung, das heißt, wenn Sie sich auf einen alten Wert des Zustands beziehen, der inzwischen geändert wurde und daher nicht mehr relevant 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:

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

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 Schalters in drei Schritten:

  1. Erhöhen des Zählers um 1

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 Schließung des Zählers erstellt haben und wenn das Timeout ausgelöst wird, setzen wir den Zähler ausgehend von seinem Wert 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);

Befolgen Sie den vollständigen 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 protokollieren, bevor wir ihn 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 Abschlüsse in JavaScript: Ein umfassender Leitfaden
    • Keyur Paralkar Lexikalischer Geltungsbereich in JavaScript – Einsteigerhandbuch
    • Souvik Paul Stale Closures in React
    • Soumya Dey: Verständnis des lexikalischen Geltungsbereichs und der Abschlüsse in JavaScript
    • Subash Mahapatra-Stackoverflow

    Das obige ist der detaillierte Inhalt vonReaktion: veralteter Abschluss. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Stellungnahme:
    Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn