Heim > Artikel > Web-Frontend > Reaktion: veralteter Abschluss
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.
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.
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:
Im folgenden Bild wird der anfängliche UI-Status der App angezeigt, mit Zähler auf Null.
Wir simulieren das Schließen des Schalters in drei Schritten:
Nach 5 Sekunden beträgt der Wert des Zählers 2.
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 (); } export default App;Closure demo
Counter value: {counter}
Follow the istructions to create a closure of the state variable counter
- Set the counter to preferred value
- Start a timeout and wait for {timeOutInSeconds} to increment the counter (current value is {counter})
- Increment by 10 the counter before the timeout
{ renderLogs() }
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)
Das obige ist der detaillierte Inhalt vonReaktion: veralteter Abschluss. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!