Nehmen wir an, ich habe einen Zustand, der von einem anderen Zustand abhängt (z. B. wenn sich A ändert, möchte ich, dass sich B ändert).
Ist es angemessen, einen Hook zu erstellen, der A beobachtet und B innerhalb des useEffect-Hooks setzt?
Werden die Effekte so kaskadiert, dass beim Klicken auf die Schaltfläche der erste Effekt ausgelöst wird, wodurch sich b ändert und der zweite Effekt vor dem nächsten Rendern ausgelöst wird? Gibt es Leistungseinbußen bei der Strukturierung von Code wie diesem?
let MyComponent = props => {
let [a, setA] = useState(1)
let [b, setB] = useState(2)
useEffect(
() => {
if (/*some stuff is true*/) {
setB(3)
}
},
[a],
)
useEffect(
() => {
// do some stuff
},
[b],
)
return (
<button
onClick={() => {
setA(5)
}}
>
click me
</button>
)
}
quelle
Im Allgemeinen wird durch die Verwendung von
setState
insideuseEffect
eine Endlosschleife erstellt, die Sie höchstwahrscheinlich nicht verursachen möchten. Es gibt einige Ausnahmen von dieser Regel, auf die ich später noch eingehen werde.useEffect
wird nach jedem Rendern aufgerufen und wennsetState
es darin verwendet wird, wird die Komponente erneut gerendert, was aufruftuseEffect
und so weiter und so fort.Einer der beliebtesten Fälle, in denen die Verwendung von
useState
inside ofuseEffect
keine Endlosschleife verursacht, ist die Übergabe eines leeren Arrays als zweites Argument anuseEffect
like.useEffect(() => {....}, [])
Dies bedeutet, dass die Effektfunktion einmal aufgerufen werden sollte: nur nach dem ersten Mount / Render. Dies wird häufig verwendet, wenn Sie Daten in einer Komponente abrufen und die Anforderungsdaten im Status der Komponente speichern möchten.quelle
setState
innenuseEffect
istsetting state
innerhalb Abonnements oder Ereignis - Listener. Aber vergessen Sie nicht, das Abonnement zu kündigen. Reactjs.org/docs/hooks-effect.html#effects-with-cleanupFür zukünftige Zwecke kann dies auch helfen:
Es ist in Ordnung, setState zu verwenden.
useEffect
Sie müssen nur wie bereits beschrieben aufpassen , um keine Schleife zu erstellen.Dies ist jedoch nicht das einzige Problem, das auftreten kann. Siehe unten:
Stellen Sie sich vor, Sie haben eine Komponente
Comp
, dieprops
vom übergeordneten Element empfangen wird, und gemäß einerprops
Änderung möchten Sie denComp
Status festlegen . Aus irgendeinem Grund müssen Sie für jede Requisite eine andere ändernuseEffect
:MACH DAS NICHT
useEffect(() => { setState({ ...state, a: props.a }); }, [props.a]); useEffect(() => { setState({ ...state, b: props.b }); }, [props.b]);
Der Status von a wird möglicherweise nie geändert, wie Sie in diesem Beispiel sehen können: https://codesandbox.io/s/confident-lederberg-dtx7w
Der Grund , warum dies in diesem Beispiel passieren , es ist , weil beide useEffects im gleichen Laufzyklus reagieren , wenn Sie beide ändern
prop.a
undprop.b
so der Wert ,{...state}
wenn Sie tun ,setState
sind genau die gleichen in beiden ,useEffect
weil sie im gleichen Kontext sind. Wenn Sie die zweite ausführensetState
, wird die erste ersetztsetState
.TUN SIE DAS STATT
Die Lösung für dieses Problem lautet im Grunde
setState
wie folgt :useEffect(() => { setState(state => ({ ...state, a: props.a })); }, [props.a]); useEffect(() => { setState(state => ({ ...state, b: props.b })); }, [props.b]);
Überprüfen Sie die Lösung hier: https://codesandbox.io/s/mutable-surf-nynlx
Jetzt erhalten Sie immer den aktuellsten und korrektesten Wert des Status, wenn Sie mit dem fortfahren
setState
.Ich hoffe das hilft jemandem!
quelle
setName(name => ({ ...name, a: props.a }));
setItems(items => [...items, item])
useEffect
kann an einer bestimmten Requisite oder einem bestimmten Zustand hängen. Das, was Sie tun müssen, um einen Endlosschleifen-Hook zu vermeiden, ist das Binden einer Variablen oder eines Zustands, um dies zu bewirkenZum Beispiel:
Der obige Effekt wird erst ausgelöst, wenn die Komponente gerendert wurde. Dies ähnelt dem
componentDidMount
Lebenszyklusconst [something, setSomething] = withState(0) const [myState, setMyState] = withState(0) useEffect(() => { setSomething(0) }, myState)
Der obige Effekt wird nur ausgelöst,
componentDidUpdate
wenn sich mein Status geändert hat. Dies ist ähnlich, außer dass nicht jeder sich ändernde Status ihn auslöst.Sie können mehr Details über diesen Link lesen
quelle
▶ 1. Kann ich den Status innerhalb eines useEffect-Hooks festlegen?
Grundsätzlich können Sie den Status frei festlegen, wo Sie ihn benötigen - auch innerhalb
useEffect
und sogar während des Renderns . Vermeiden Sie Endlosschleifen, indem Sie den Hakendeps
richtig einstellen und / oder bedingt angeben .▶ 2. Nehmen wir an, ich habe einen Zustand, der von einem anderen Zustand abhängt. Ist es angemessen, einen Hook zu erstellen, der A beobachtet und B innerhalb des useEffect-Hooks setzt?
Sie haben gerade den klassischen Anwendungsfall beschrieben für
useReducer
:Code-Snippet anzeigen
let MyComponent = () => { let [state, dispatch] = useReducer(reducer, { a: 1, b: 2 }); useEffect(() => { console.log("Some effect with B"); }, [state.b]); return ( <div> <p>A: {state.a}, B: {state.b}</p> <button onClick={() => dispatch({ type: "SET_A", payload: 5 })}> Set A to 5 and Check B </button> <button onClick={() => dispatch({ type: "INCREMENT_B" })}> Increment B </button> </div> ); }; // B depends on A. If B >= A, then reset B to 1. function reducer(state, { type, payload }) { const someCondition = state.b >= state.a; if (type === "SET_A") return someCondition ? { a: payload, b: 1 } : { ...state, a: payload }; else if (type === "INCREMENT_B") return { ...state, b: state.b + 1 }; return state; } ReactDOM.render(<MyComponent />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script> <div id="root"></div> <script>var { useReducer, useEffect } = React</script>
▶ 3. Werden die Effekte so kaskadiert, dass beim Klicken auf die Schaltfläche der erste Effekt ausgelöst wird, wodurch sich b ändert und der zweite Effekt vor dem nächsten Rendern ausgelöst wird?
useEffect
Wird immer ausgeführt, nachdem das Rendern festgeschrieben und DOM-Änderungen angewendet wurden. Der erste Effekt wird ausgelöst, geändertb
und bewirkt ein erneutes Rendern. Nach Abschluss dieses Renderns wird der zweite Effekt aufgrund vonb
Änderungen ausgeführt.Code-Snippet anzeigen
let MyComponent = props => { console.log("render"); let [a, setA] = useState(1); let [b, setB] = useState(2); let isFirstRender = useRef(true); useEffect(() => { console.log("useEffect a, value:", a); if (isFirstRender.current) isFirstRender.current = false; else setB(3); return () => { console.log("unmount useEffect a, value:", a); }; }, [a]); useEffect(() => { console.log("useEffect b, value:", b); return () => { console.log("unmount useEffect b, value:", b); }; }, [b]); return ( <div> <p>a: {a}, b: {b}</p> <button onClick={() => { console.log("Clicked!"); setA(5); }} > click me </button> </div> ); }; ReactDOM.render(<MyComponent />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script> <div id="root"></div> <script>var { useReducer, useEffect, useState, useRef } = React</script>
▶ 4. Gibt es Leistungseinbußen bei der Strukturierung von Code wie diesem?
Ja. Durch das Umschließen der Statusänderung
b
in ein separatesuseEffect
füra
verfügt der Browser über eine zusätzliche Layout- / Malphase - diese Effekte sind möglicherweise für den Benutzer sichtbar. Wenn Sie es auf keinen FalluseReducer
versuchen möchten , können Sie denb
Status zusammen mita
direkt ändern :Code-Snippet anzeigen
let MyComponent = () => { console.log("render"); let [a, setA] = useState(1); let [b, setB] = useState(2); useEffect(() => { console.log("useEffect b, value:", b); return () => { console.log("unmount useEffect b, value:", b); }; }, [b]); const handleClick = () => { console.log("Clicked!"); setA(5); b >= 5 ? setB(1) : setB(b + 1); }; return ( <div> <p> a: {a}, b: {b} </p> <button onClick={handleClick}>click me</button> </div> ); }; ReactDOM.render(<MyComponent />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script> <div id="root"></div> <script>var { useReducer, useEffect, useState, useRef } = React</script>
quelle