Betrachtet man unten das Hakenbeispiel
import { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
Grundsätzlich verwenden wir die Methode this.forceUpdate (), um zu erzwingen, dass die Komponente in React-Klassenkomponenten wie im folgenden Beispiel sofort neu gerendert wird
class Test extends Component{
constructor(props){
super(props);
this.state = {
count:0,
count2: 100
}
this.setCount = this.setCount.bind(this);//how can I do this with hooks in functional component
}
setCount(){
let count = this.state.count;
count = count+1;
let count2 = this.state.count2;
count2 = count2+1;
this.setState({count});
this.forceUpdate();
//before below setState the component will re-render immediately when this.forceUpdate() is called
this.setState({count2: count
}
render(){
return (<div>
<span>Count: {this.state.count}></span>.
<button onClick={this.setCount}></button>
</div>
}
}
Meine Frage lautet jedoch: Wie kann ich die oben genannte Funktionskomponente zwingen, sofort mit Hooks neu zu rendern?
javascript
reactjs
react-native
react-hooks
Hemadri Dasari
quelle
quelle
this.forceUpdate()
? Vielleicht gibt es einen Weg, dasselbe ohne das zu erreichen.Antworten:
Dies ist möglich , mit
useState
oderuseReducer
, dauseState
AnwendungenuseReducer
intern :const [, updateState] = React.useState(); const forceUpdate = React.useCallback(() => updateState({}), []);
forceUpdate
ist nicht für den normalen Gebrauch vorgesehen, nur für Tests oder andere offene Fälle. Diese Situation kann auf konventionellere Weise angegangen werden.setCount
ist ein Beispiel für eine nicht ordnungsgemäße VerwendungforceUpdate
,setState
ist aus Leistungsgründen asynchron und sollte nicht zur Synchronisierung gezwungen werden, nur weil Statusaktualisierungen nicht korrekt durchgeführt wurden. Wenn sich ein Status auf den zuvor festgelegten Status stützt, sollte dies mit der Updater-Funktion erfolgen .setCount
Dies ist möglicherweise kein anschauliches Beispiel, da sein Zweck unklar ist. Dies gilt jedoch für die Updater-Funktion:setCount(){ this.setState(({count}) => ({ count: count + 1 })); this.setState(({count2}) => ({ count2: count + 1 })); this.setState(({count}) => ({ count2: count + 1 })); }
Dies wird 1: 1 in Hooks übersetzt, mit der Ausnahme, dass Funktionen, die als Rückrufe verwendet werden, besser gespeichert werden sollten:
const [state, setState] = useState({ count: 0, count2: 100 }); const setCount = useCallback(() => { setState(({count}) => ({ count: count + 1 })); setState(({count2}) => ({ count2: count + 1 })); setState(({count}) => ({ count2: count + 1 })); }, []);
quelle
const forceUpdate = useCallback(() => updateState({}), []);
? Erzwingt es überhaupt ein Update?useCallback
merkt sichforceUpdate
, so dass es während der Lebensdauer der Komponenten konstant bleibt und sicher als Requisite übergeben werden kann.updateState({})
Aktualisiert den Status bei jedemforceUpdate
Aufruf mit einem neuen Objekt . Dies führt zu einem erneuten Rendern. Also ja, es erzwingt ein Update, wenn es aufgerufen wird.useCallback
Teil ist also nicht wirklich notwendig. Es sollte ohne es gut funktionieren.updateState(0)
weil0
es sich um einen primitiven Datentyp handelt? Muss es ein Objekt sein oder würdeupdateState([])
(dh die Verwendung mit einem Array) auch funktionieren?updateState(Math.random())
ein Zähler verwendet werden.Wie die anderen bereits erwähnt haben,
useState
funktioniert - so implementiert mobx-react-lite Updates - können Sie etwas Ähnliches tun.Definieren Sie einen neuen Haken,
useForceUpdate
-import { useState, useCallback } from 'react' export function useForceUpdate() { const [, setTick] = useState(0); const update = useCallback(() => { setTick(tick => tick + 1); }, []) return update; }
und verwenden Sie es in einer Komponente -
const forceUpdate = useForceUpdate(); if (...) { forceUpdate(); // force re-render }
Siehe https://github.com/mobxjs/mobx-react-lite/blob/master/src/utils.ts und https://github.com/mobxjs/mobx-react-lite/blob/master/src/useObserver .ts
quelle
useForceUpdate
Soweit ich von Hooks weiß, funktioniert dies möglicherweise nicht, da bei jedem erneuten Rendern der Funktion eine neue Funktion zurückgegeben wird. DamitforceUpdate
es funktioniert, wenn es in a verwendet wirduseEffect
, sollte es zurückgegeben werden.useCallback(update)
Siehe kentcdodds.com/blog/usememo-and-usecallbackIm Allgemeinen können Sie jeden Statusbehandlungsansatz verwenden, mit dem Sie ein Update auslösen möchten.
Mit TypeScript
Code und Box Beispiel
useState
const forceUpdate: () => void = React.useState()[1].bind(null, {}) // see NOTE below
useReducer
const forceUpdate = React.useReducer(() => ({}), {})[1] as () => void
als benutzerdefinierter Haken
Wickeln Sie einfach den von Ihnen bevorzugten Ansatz so ein
function useForceUpdate(): () => void { return React.useReducer(() => ({}), {})[1] as () => void // <- paste here }
Wie funktioniert das?
" Ein Update auslösen " bedeutet, der React Engine mitzuteilen, dass sich ein Wert geändert hat und dass Ihre Komponente erneut gerendert werden soll.
[, setState]
fromuseState()
erfordert einen Parameter. Wir werden es los, indem wir ein neues Objekt binden{}
.() => ({})
inuseReducer
ist ein Dummy-Reduzierer, der jedes Mal, wenn eine Aktion ausgelöst wird, ein neues Objekt zurückgibt.{}
(frisches Objekt) ist erforderlich, damit eine Aktualisierung durch Ändern einer Referenz im Status ausgelöst wird.PS:
useState
Wraps nuruseReducer
intern. QuelleHINWEIS: Die Verwendung von .bind mit useState führt zu einer Änderung der Funktionsreferenz zwischen den Renderings. Es ist möglich, es in useCallback zu verpacken, wie hier bereits erklärt , aber dann wäre es kein sexy Einzeiler ™ . Die Reducer-Version behält bereits die Referenzgleichheit zwischen Renderings bei. Dies ist wichtig, wenn Sie die forceUpdate-Funktion in Requisiten übergeben möchten.
einfache JS
const forceUpdate = React.useState()[1].bind(null, {}) // see NOTE above const forceUpdate = React.useReducer(() => ({}))[1]
quelle
Alternative zu @ MinhKhas Antwort:
Es kann viel sauberer sein mit
useReducer
:const [, forceUpdate] = useReducer(x => x + 1, 0);
Verwendung:
forceUpdate()
- Reiniger ohne Parameterquelle
Sie können den useState einfach so definieren:
const [, forceUpdate] = React.useState(0);
Und Verwendung:
forceUpdate(n => !n)
Ich hoffe das hilft !
quelle
Sie sollten Ihre Komponente vorzugsweise nur vom Status und den Requisiten abhängig machen, damit sie wie erwartet funktioniert. Wenn Sie jedoch wirklich eine Funktion benötigen, um das erneute Rendern der Komponente zu erzwingen, können Sie den
useState
Hook verwenden und die Funktion bei Bedarf aufrufen.Beispiel
const { useState, useEffect } = React; function Foo() { const [, forceUpdate] = useState(); useEffect(() => { setTimeout(forceUpdate, 2000); }, []); return <div>{Date.now()}</div>; } ReactDOM.render(<Foo />, document.getElementById("root"));
<script src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script> <script src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script> <div id="root"></div>
quelle
forceUpdate()
und nur vonthis.props
undthis.state
in zu vermeidenrender()
."and I can't think of a good use case for it right now
Ich habe eine, wie wäre es, wenn der Status nicht von React gesteuert wird. Ich benutze Redux nicht, aber ich gehe davon aus, dass es eine Art Forceupdate durchführen muss. Ich persönlich verwende Proxys, um den Status beizubehalten. Die Komponente kann dann nach Änderungen an den Requisiten suchen und diese dann aktualisieren. Scheint auch sehr effizient zu arbeiten. Beispielsweise werden alle meine Komponenten dann von einem Proxy gesteuert. Dieser Proxy wird von SessionStorage unterstützt, sodass selbst wenn der Benutzer seine Webseite aktualisiert, der Status gleichmäßiger Dropdowns usw. beibehalten wird. IOW: Ich benutze überhaupt keinen Status, alles wird mit Requisiten gesteuert.React Hooks FAQ offizielle Lösung für
forceUpdate
:const [_, forceUpdate] = useReducer((x) => x + 1, 0); // usage <button onClick={forceUpdate}>Force update</button>
Arbeitsbeispiel
Code-Snippet anzeigen
const App = () => { const [_, forceUpdate] = useReducer((x) => x + 1, 0); return ( <div> <button onClick={forceUpdate}>Force update</button> <p>Forced update {_} times</p> </div> ); }; ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.1/umd/react.production.min.js" integrity="sha256-vMEjoeSlzpWvres5mDlxmSKxx6jAmDNY4zCt712YCI0=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.1/umd/react-dom.production.min.js" integrity="sha256-QQt6MpTdAD0DiPLhqhzVyPs1flIdstR4/R7x4GqCvZ4=" crossorigin="anonymous"></script> <script>var useReducer = React.useReducer</script> <div id="root"></div>
quelle
Einfacher Code
const forceUpdate = React.useReducer(bool => !bool)[1];
Verwenden:
quelle
Sie können (ab) normale Hooks verwenden, um ein erneutes Rendern zu erzwingen, indem Sie die Tatsache ausnutzen, dass React keine Booleschen Werte in JSX-Code druckt
// create a hook const [forceRerender, setForceRerender] = React.useState(true); // ...put this line where you want to force a rerender setForceRerender(!forceRerender); // ...make sure that {forceRerender} is "visible" in your js code // ({forceRerender} will not actually be visible since booleans are // not printed, but updating its value will nonetheless force a // rerender) return ( <div>{forceRerender}</div> )
quelle
Mögliche Option ist, die Aktualisierung nur für bestimmte Komponenten zu erzwingen
key
. Das Aktualisieren des Schlüssels löst ein Rendering der Komponente aus (die zuvor nicht aktualisiert werden konnte).Zum Beispiel:
const [tableKey, setTableKey] = useState(1); ... useEffect(() => { ... setTableKey(tableKey + 1); }, [tableData]); ... <DataTable key={tableKey} data={tableData}/>
quelle
Lösung in einer einzigen Zeile:
const [,forceRender] = useReducer((s) => s+1, 0)
Hier erfahren Sie mehr über useReducer. https://reactjs.org/docs/hooks-reference.html#usereducer
quelle
Einzeilige Lösung:
const useForceUpdate = () => useState()[1];
useState gibt ein Wertepaar zurück: den aktuellen Status und eine Funktion, die ihn aktualisiert - Status und Setter . Hier verwenden wir nur den Setter, um ein erneutes Rendern zu erzwingen.
quelle
Meine Variation von
forceUpdate
erfolgt nicht über eincounter
, sondern über ein Objekt:// Emulates `forceUpdate()` const [unusedState, setUnusedState] = useState() const forceUpdate = useCallback(() => setUnusedState({}), [])
Weil
{} !== {}
jedes Mal.quelle
useCallback()
? Woher kommt das? Hoppla. Ich sehe es jetzt ...Dadurch werden abhängige Komponenten dreimal gerendert (Arrays mit gleichen Elementen sind nicht gleich):
const [msg, setMsg] = useState([""]) setMsg(["test"]) setMsg(["test"]) setMsg(["test"])
quelle
Informationen zu regulären Komponenten, die auf React Class basieren, finden Sie unter React Docs für die
forceUpdate
API unter dieser URL. Die Dokumente erwähnen Folgendes:In den Dokumenten wird jedoch auch erwähnt, dass:
Obwohl Anwendungsfälle für die Verwendung
forceUpdate
selten sind und ich sie noch nie verwendet habe, habe ich gesehen, dass sie von anderen Entwicklern in einigen älteren Unternehmensprojekten verwendet wurden, an denen ich gearbeitet habe.Informationen zu den entsprechenden Funktionen für Funktionskomponenten finden Sie in den React Docs for HOOKS unter dieser URL. Gemäß der obigen URL kann der Hook "useReducer" verwendet werden, um eine
forceUpdate
Funktionalität für Funktionskomponenten bereitzustellen .Im
that does not use state or props
Folgenden finden Sie ein funktionierendes Codebeispiel , das auch in CodeSandbox unter dieser URL verfügbar istimport React, { useReducer, useRef } from "react"; import ReactDOM from "react-dom"; import "./styles.css"; function App() { // Use the useRef hook to store a mutable value inside a functional component for the counter let countref = useRef(0); const [, forceUpdate] = useReducer(x => x + 1, 0); function handleClick() { countref.current++; console.log("Count = ", countref.current); forceUpdate(); // If you comment this out, the date and count in the screen will not be updated } return ( <div className="App"> <h1> {new Date().toLocaleString()} </h1> <h2>You clicked {countref.current} times</h2> <button onClick={() => { handleClick(); }} > ClickToUpdateDateAndCount </button> </div> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
HINWEIS: Unter dieser URL ist auch ein alternativer Ansatz mit dem useState-Hook (anstelle von useReducer) verfügbar .
quelle
react-tidy
hat einen benutzerdefinierten Haken nur dafüruseRefresh
:import React from 'react' import {useRefresh} from 'react-tidy' function App() { const refresh = useRefresh() return ( <p> The time is {new Date()} <button onClick={refresh}>Refresh</button> </p> ) }
Erfahren Sie mehr über diesen Haken
Haftungsausschluss Ich bin der Autor dieser Bibliothek.
quelle