Möchten Sie eine Funktion anfordern oder einen Fehler melden?
Insekt
Wie ist das aktuelle Verhalten?
Ich kann mich nicht auf Daten aus der Kontext-API verlassen, indem ich (useContext-Hook) verwende, um unnötige Neudarstellungen mit React.memo zu verhindern
Wenn das aktuelle Verhalten ein Fehler ist, stellen Sie bitte die Schritte zur Reproduktion und wenn möglich eine minimale Demo des Problems bereit. Fügen Sie den Link zu Ihrem JSFiddle (https://jsfiddle.net/Luktwrdm/) oder CodeSandbox (https://codesandbox.io/s/new) Beispiel unten ein:
React.memo(() => {
const [globalState] = useContext(SomeContext);
render ...
}, (prevProps, nextProps) => {
// How to rely on context in here?
// I need to rerender component only if globalState contains nextProps.value
});
Was ist das erwartete Verhalten?
Ich sollte irgendwie Zugriff auf den Kontext im zweiten Argument-Callback von React.memo haben, um das Rendern zu verhindern
Oder ich sollte die Möglichkeit haben, eine alte Instanz der Reaktionskomponente im Funktionsrumpf zurückzugeben.
Welche React-Versionen und welche Browser/Betriebssysteme sind von diesem Problem betroffen?
16.8.4
Dies funktioniert wie geplant. Es gibt eine längere Diskussion darüber in https://github.com/facebook/react/issues/14110, wenn Sie neugierig sind.
Nehmen wir an, Sie haben aus irgendeinem Grund AppContext
dessen Wert eine theme
Eigenschaft hat, und Sie möchten nur einige ExpensiveTree
bei appContextValue.theme
Änderungen neu rendern.
TLDR bedeutet, dass Sie im Moment drei Optionen haben:
Wenn wir nur appContextValue.theme
in vielen Komponenten benötigen, sich aber appContextValue
selbst zu oft ändert, könnten wir ThemeContext
von AppContext
aufteilen.
function Button() {
let theme = useContext(ThemeContext);
// The rest of your rendering logic
return <ExpensiveTree className={theme} />;
}
Jetzt werden bei jeder Änderung von AppContext
ThemeContext
Verbraucher nicht erneut gerendert.
Dies ist die bevorzugte Lösung. Dann brauchen Sie kein spezielles Rettungspaket.
memo
dazwischenWenn Sie Kontexte aus irgendeinem Grund nicht aufteilen können, können Sie das Rendering dennoch optimieren, indem Sie eine Komponente in zwei Teile aufteilen und spezifischere Requisiten an die innere übergeben. Sie würden immer noch das äußere rendern, aber es sollte billig sein, da es nichts tut.
function Button() {
let appContextValue = useContext(AppContext);
let theme = appContextValue.theme; // Your "selector"
return <ThemedButton theme={theme} />
}
const ThemedButton = memo(({ theme }) => {
// The rest of your rendering logic
return <ExpensiveTree className={theme} />;
});
useMemo
darinSchließlich könnten wir unseren Code etwas ausführlicher gestalten, ihn aber in einer einzigen Komponente belassen, indem wir den Rückgabewert in useMemo
umschließen und seine Abhängigkeiten angeben. Unsere Komponente würde immer noch erneut ausgeführt, aber React würde den untergeordneten Baum nicht erneut rendern, wenn alle useMemo
Eingaben gleich sind.
function Button() {
let appContextValue = useContext(AppContext);
let theme = appContextValue.theme; // Your "selector"
return useMemo(() => {
// The rest of your rendering logic
return <ExpensiveTree className={theme} />;
}, [theme])
}
In Zukunft könnte es noch mehr Lösungen geben, aber die haben wir jetzt.
Beachten Sie jedoch, dass Option 1 vorzuziehen ist – wenn sich der Kontext zu oft ändert, sollten Sie ihn aufteilen .
Bei beiden Optionen wird das Rendern von Kindern verhindert, wenn sich das Thema nicht geändert hat.
@gaearon Sind die Buttons die Kinder oder rendern die Buttons Kinder? Mir fehlt etwas Kontext, wie diese verwendet werden.
Die Verwendung der Option unstable_Profiler
2 löst immer noch onRender
Callbacks aus, ruft jedoch nicht die eigentliche Renderlogik auf. Vielleicht mache ich etwas falsch? ~ https://codesandbox.io/s/kxz4o2oyoo~ https://codesandbox.io/s/00yn9yqzjw
Ich habe das Beispiel aktualisiert, um es klarer zu machen.
Die Verwendung der unstable_Profiler-Option 2 löst weiterhin onRender-Callbacks aus, ruft jedoch nicht die eigentliche Renderlogik auf. Vielleicht mache ich etwas falsch? https://codesandbox.io/s/kxz4o2oyoo
Genau das ist der Sinn dieser Option. :-)
Vielleicht wäre eine gute Lösung dafür, die Möglichkeit zu haben, den Kontext zu "nehmen" und die Komponente nur dann neu zu rendern, wenn der Rückruf true zurückgibt, z.
useContext(ThemeContext, (contextData => contextData.someArray.length !== 0 ));
Das Hauptproblem bei Hooks, das ich tatsächlich kennengelernt habe, ist, dass wir nicht innerhalb eines Hooks verwalten können, was von einer Komponente zurückgegeben wird - um das Rendern zu verhindern, den gespeicherten Wert zurückzugeben usw.
Wenn wir könnten, wäre es nicht komponierbar.
https://overreacted.io/why-isnt-xa-hook/#not -a-hook-usebailout
Option 4: Verwenden Sie keinen Kontext für die Datenweitergabe, sondern ein Datenabonnement. Verwenden Sie useSubscription (weil es schwer ist, alle Fälle abzudecken).
Es gibt eine andere Möglichkeit, das erneute Rendern zu vermeiden.
"Sie müssen den JSX eine Stufe höher aus der Re-Rendering-Komponente verschieben, dann wird er nicht jedes Mal neu erstellt."
Vielleicht wäre eine gute Lösung dafür, die Möglichkeit zu haben, den Kontext zu "nehmen" und die Komponente nur dann neu zu rendern, wenn der Rückruf true zurückgibt, z.
useContext(ThemeContext, (contextData => contextData.someArray.length !== 0 ));
Das Hauptproblem bei Hooks, das ich tatsächlich kennengelernt habe, ist, dass wir nicht innerhalb eines Hooks verwalten können, was von einer Komponente zurückgegeben wird - um das Rendern zu verhindern, den gespeicherten Wert zurückzugeben usw.
Anstelle von wahr/falsch hier... könnten wir eine identitätsbasierte Funktion bereitstellen, die es uns ermöglicht, die Daten aus dem Kontext zu subsetieren?
const contextDataINeed = useContext(ContextObj, (state) => state['keyICareAbout'])
wobei useContext nicht in dieser Komponente erscheinen würde, es sei denn, das Ergebnis des Selektors fn unterschied sich in Bezug auf die Identität vom vorherigen Ergebnis derselben Funktion.
fand diese Bibliothek, dass dies die Lösung für Facebook sein könnte, um Hooks zu integrieren https://blog.axlight.com/posts/super-performant-global-state-with-react-context-and-hooks/
Es gibt eine andere Möglichkeit, das erneute Rendern zu vermeiden.
"Sie müssen den JSX eine Stufe höher aus der Re-Rendering-Komponente verschieben, dann wird er nicht jedes Mal neu erstellt."
Das Problem ist, dass es kostspielig sein kann, den Komponentenbaum umzustrukturieren, nur um ein erneutes Rendern von oben nach unten zu verhindern.
@fuleinist Letztendlich unterscheidet es sich nicht so sehr von MobX, obwohl es für einen bestimmten Anwendungsfall stark vereinfacht ist. MobX funktioniert bereits so (auch unter Verwendung von Proxies), der Zustand wird mutiert und Komponenten, die bestimmte Bits des Zustands verwenden, werden neu gerendert, sonst nichts.
@gaearon Ich weiß nicht, ob mir etwas fehlt, aber ich habe deine zweite und dritte Option ausprobiert und sie funktionieren nicht richtig. Ich bin mir nicht sicher, ob dies nur ein Fehler bei der Chrome-Erweiterung ist oder ob es einen anderen Haken gibt. Hier ist mein einfaches Formularbeispiel, bei dem ich beide Eingaben neu rendern sehe. In der Konsole sehe ich, dass Memo seinen Job macht, aber DOM ständig neu gerendert wird. Ich habe 1000 Elemente ausprobiert und das onChange-Ereignis ist wirklich langsam, deshalb denke ich, dass memo() nicht richtig mit dem Kontext funktioniert. Danke für jeden Rat:
Hier ist eine Demo mit 1000 Elementen/Textfeldern. Aber in dieser Demo zeigen die Entwicklertools kein erneutes Rendern an. Sie müssen lokale Quellen herunterladen, um es zu testen: https://codesandbox.io/embed/zen-firefly-d5bxk
import React, { createContext, useState, useContext, memo } from "react";
const FormContext = createContext();
const FormProvider = ({ initialValues, children }) => {
const [values, setValues] = useState(initialValues);
const value = {
values,
setValues
};
return <FormContext.Provider value={value}>{children}</FormContext.Provider>;
};
const TextField = memo(
({ name, value, setValues }) => {
console.log(name);
return (
<input
type="text"
value={value}
onChange={e => {
e.persist();
setValues(prev => ({
...prev,
[name]: e.target.value
}));
}}
/>
);
},
(prev, next) => prev.value === next.value
);
const Field = ({ name }) => {
const { values, setValues } = useContext(FormContext);
const value = values[name];
return <TextField name={name} value={value} setValues={setValues} />;
};
const App = () => (
<FormProvider initialValues={{ firstName: "Marr", lastName: "Keri" }}>
First name: <Field name="firstName" />
<br />
Last name: <Field name="lastName" />
</FormProvider>
);
export default App;
Auf der anderen Seite funktioniert dieser Ansatz ohne Kontext korrekt, ist immer noch im Debugging langsamer als ich erwartet hatte, aber zumindest ist das erneute Rendern in Ordnung
import React, { useState, memo } from "react";
import ReactDOM from "react-dom";
const arr = [...Array(1000).keys()];
const TextField = memo(
({ index, value, onChange }) => (
<input
type="text"
value={value}
onChange={e => {
console.log(index);
onChange(index, e.target.value);
}}
/>
),
(prev, next) => prev.value === next.value
);
const App = () => {
const [state, setState] = useState(arr.map(x => ({ name: x })));
const onChange = (index, value) =>
setState(prev => {
return prev.map((item, i) => {
if (i === index) return { name: value };
return item;
});
});
return state.map((item, i) => (
<div key={i}>
<TextField index={i} value={item.name} onChange={onChange} />
</div>
));
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
@marrkeri Ich sehe im ersten Code-Snippet nichts Falsches. Die in den Entwicklungstools hervorgehobene Komponente ist Field
, die den Kontext verwendet, nicht die TextField
die eine Memokomponente ist und die areEqual-Funktion implementiert.
Ich denke, das Leistungsproblem im Codesandbox-Beispiel kommt von den 1000 Komponenten, die den Kontext verwenden. Refaktorieren Sie es in eine Komponente, die den Kontext verwendet, sagen wir Fields
, und geben Sie von dieser Komponente (mit einer Zuordnung) ein TextField
für jeden Wert zurück.
@marrkeri Ich sehe im ersten Code-Snippet nichts Falsches. Die in den Entwicklungstools hervorgehobene Komponente ist
Field
, die den Kontext verwendet, nicht dieTextField
die eine Memokomponente ist und die areEqual-Funktion implementiert.Ich denke, das Leistungsproblem im Codesandbox-Beispiel kommt von den 1000 Komponenten, die den Kontext verwenden. Refaktorieren Sie es in eine Komponente, die den Kontext verwendet, sagen wir
Fields
, und geben Sie von dieser Komponente (mit einer Zuordnung) einTextField
für jeden Wert zurück.
Wie du sagtest, war ich unter dem gleichen Gedanken, dass
Ich habe Ihren zweiten Punkt zum Refactoring auf eine Komponente nicht verstanden
@marrkeri Ich habe so etwas vorgeschlagen: https://codesandbox.io/s/little-night-p985y.
Ist dies der Grund, warum React-Redux bei der Migration zu Hooks aufhören musste , die Vorteile der Stable-Context-API zu nutzen und den aktuellen Status an den Kontext weiterzugeben?
Scheint also so zu sein
Dies ist möglicherweise die einzige Option, wenn Sie die Verwendungen nicht kontrollieren (für Optionen 2-3 erforderlich) und nicht alle möglichen Selektoren aufzählen können (für Option 1 erforderlich), aber dennoch eine Hooks-API verfügbar machen möchten
const MyContext = createContext()
export const Provider = ({children}) => (
<MyContext.provider value={{subscribe: listener => ..., getValue: () => ...}}>
{children}
</MyContext.provider>
)
export const useSelector = (selector, equalityFunction = (a, b) => a === b) => {
const {subscribe, getValue} = useContext(MyContext)
const [value, setValue] = useState(getValue())
useEffect(() => subscribe(state => {
const newValue = selector(state)
if (!equalityFunction(newValue, value) {
setValue(newValue)
}
}), [selector, equalityFunction])
}
@Hypnosphi : Wir haben aufgehört, den Store-Zustand im Kontext zu übergeben (die v6-Implementierung) und sind zurück zu direkten Store-Abonnements (die v7-Implementierung) gewechselt, aufgrund einer Kombination von Leistungsproblemen und der Unfähigkeit, kontextbedingte Updates zu retten (was es machte unmöglich, eine React-Redux-Hooks-API basierend auf dem v6-Ansatz zu erstellen).
Weitere Informationen finden Sie in meinem Beitrag Die Geschichte und Implementierung von React-Redux .
Ich habe den Thread durchgelesen, aber ich frage mich immer noch - sind die einzigen Optionen, die heute verfügbar sind, um bei Kontextänderung bedingt neu zu rendern, "Optionen 1 2 3 4" oben aufgeführt? Ist etwas anderes in Arbeit, um dies offiziell anzugehen, oder werden die "4 Lösungen" als akzeptabel genug angesehen?
Ich habe im anderen Thread geschrieben, aber nur für den Fall. Hier ist eine _inoffizielle_ Problemumgehung.
useContextSelector- Vorschlag und use-context-selector- Bibliothek im Userland.
ehrlich gesagt, das lässt mich denken, dass das Framework einfach nicht bereit ist, vollständig in Funktionen und Hooks einzusteigen, wie wir es erwarten. Mit Klassen und Lebenszyklusmethoden hatte man eine ordentliche Möglichkeit, diese Dinge zu kontrollieren, und jetzt mit Hooks ist es eine viel schlechtere, weniger lesbare Syntax
Option 3 scheint nicht zu funktionieren. Mache ich etwas falsch? https://stackblitz.com/edit/react-w8gr8z
@Martin , dem
Das Hakenmuster kann mit gut organisierter Dokumentation und Code gelesen werden
Struktur und Reaktionsklassen und Lebenszyklus sind alle durch funktionale ersetzbar
gleichwertige.
Leider kann das reaktive Muster nicht durch React Along via . erreicht werden
entweder React-Klassen oder Hooks.
Am Sa, 11. Januar 2020 um 9:44 Uhr Martin Genev [email protected]
schrieb:
ehrlich gesagt, das lässt mich denken, dass das Framework einfach nicht einsatzbereit ist
vollständig in Funktionen und Hooks ein, wie wir dazu ermutigt werden. Mit Klassen
und Lifecycle-Methoden hatten Sie eine saubere Möglichkeit, diese Dinge zu kontrollieren und
Jetzt mit Hooks ist es eine viel schlimmere, weniger lesbare Syntax—
Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/facebook/react/issues/15156?email_source=notifications&email_token=AAI4DWUJ7WUXMHAR6F2KVXTQ5D25TA5CNFSM4G7UEEO2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMV2HJKTLV
oder abmelden
https://github.com/notifications/unsubscribe-auth/AAI4DWUCO7ORHV5OSDCE35TQ5D25TANCNFSM4G7UEEOQ
.
@mgenev Option 3 verhindert das erneute Rendern des Kindes ( <ExpensiveTree />
, Name spricht für sich selbst)
@Hypnosphi danke, das war richtig.
https://stackblitz.com/edit/react-ycfyye
Ich habe umgeschrieben und jetzt werden die eigentlichen Rendering-(Anzeige-)Komponenten nicht neu gerendert, aber alle Container (mit dem Kontext verbunden) rendern bei jeder Änderung der Requisite im Kontext, egal ob sie in ihnen verwendet wird. Die einzige Option, die ich sehen kann, besteht darin, die Kontexte aufzuteilen, aber einige Dinge sind wirklich global und werden auf höchster Ebene umbrochen, und eine Änderung an einer Requisite in ihnen führt dazu, dass alle Container in der gesamten App ausgelöst werden. Verstehe nicht, wie das für die Leistung gut sein kann...
Gibt es irgendwo ein gutes Beispiel dafür, wie man das performant macht? Die offiziellen Dokumente sind wirklich begrenzt
Sie können meine Option 4 ausprobieren, die im Grunde das ist, was React-Redux intern macht
https://github.com/facebook/react/issues/15156#issuecomment -546703046
Um die Funktion subscribe
zu implementieren, können Sie etwas wie Observables oder EventEmitter verwenden oder einfach selbst eine grundlegende Abonnementlogik schreiben:
function StateProvider({children}) {
const [state, dispatch] = useReducer(reducer, initialState)
const listeners = useRef([])
const subscribe = listener => {
listeners.current.push(listener)
}
useEffect(() => {
listeners.current.forEach(listener => listener(state)
}, [state])
return (
<DispatchContext.Provider value={dispatch}>
<SubscribeContext.Provider value={{subscribe, getValue: () => state}}>
{children}
</SubscribeContext.Provider>
</DispatchContext.Provider>
);
}
Für diejenigen, die Interesse haben, hier der Vergleich verschiedener Bibliotheken, die etwas mit diesem Thema zu tun haben.
https://github.com/dai-shi/will-this-react-global-state-work-in-concurrent-mode
Meine letzte Anstrengung besteht darin, die Unterstützung von State Branching mit useTransition
zu überprüfen, die im kommenden Concurrent Mode wichtig sein würde.
Grundsätzlich ist es in Ordnung, wenn wir React-Zustand und -Kontext auf normale Weise verwenden. (Ansonsten brauchen wir einen Trick, zum Beispiel diesen .)
Danke @dai-shi Deine Pakete gefallen mir sehr gut und ich überlege sie zu übernehmen.
@dai-shi hallo, ich habe gerade deine react-tracked
Bibliothek gefunden und sie sieht wirklich gut aus, wenn sie Leistungsprobleme mit Kontexten löst, wie sie es verspricht. Ist es noch aktuell oder besser etwas anderes? Hier sehe ich ein gutes Beispiel für seine Verwendung und zeigt auch, wie man mit use-reducer-async
Middleware-Niveau macht https://github.com/dai-shi/react-tracked/blob/master/examples/12_async/src/store .ts Danke dafür. Derzeit habe ich etwas Ähnliches mit useReducer
, dispatch
mit meiner eigenen für asynchrone Middleware umschlossen und Context
aber ich habe Bedenken wegen zukünftiger Rendering-Leistungsprobleme aufgrund von Kontextumbrüchen.
@bobrosoft react-tracked
ist ziemlich stabil, würde ich sagen (sicherlich als ein Entwicklerprodukt). Feedback ist sehr willkommen und so würde eine Bibliothek verbessert. Derzeit verwendet es intern ein nicht dokumentiertes Feature von React, und ich hoffe, wir können es in Zukunft durch ein besseres Primitiv ersetzen. use-reducer-async
ist fast wie ein einfacher Syntaxzucker, der nie schief gehen würde.
Dieser HoC hat bei mir funktioniert:
````
import React, { useMemo, ReactElement, FC } from 'react';
Import von 'lodash/reduce' reduzieren;
type Selector = (Kontext: beliebig) => beliebig;
Schnittstelle SelectorObject {
}
const withContext = (
Komponente: FC,
Kontext: beliebig,
Selektoren: SelectorObject,
): FC => {
return (props: any): ReactElement => {
const Consumer = ({ context }: any): ReactElement => {
const contextProps = reduzieren(
Selektoren,
(acc: any, selector: Selector, key: string): any => {
const-Wert = Selektor(Kontext);
acc[key] = Wert;
Rücksendung gem;
},
{},
);
zurück useMemo(
(): ReactElement =>
[...Object.values(props), ...Object.values(contextProps)],
);
};
Rückkehr (
{(Kontext: beliebig): ReactElement =>
);
};
};
Standard mit Kontext exportieren;
````
Anwendungsbeispiel:
export default withContext(Component, Context, {
value: (context): any => context.inputs.foo.value,
status: (context): any => context.inputs.foo.status,
});
Dies könnte als das Kontextäquivalent von redux mapStateToProps angesehen werden
Ich habe einen hoc erstellt, der in Redux fast sehr ähnlich zu connect() ist
const withContext = (
context = createContext(),
mapState,
mapDispatchers
) => WrapperComponent => {
function EnhancedComponent(props) {
const targetContext = useContext(context);
const { ...statePointers } = mapState(targetContext);
const { ...dispatchPointers } = mapDispatchers(targetContext);
return useMemo(
() => (
<WrapperComponent {...props} {...statePointers} {...dispatchPointers} />
),
[
...Object.values(statePointers),
...Object.values(props),
...Object.values(dispatchPointers)
]
);
}
return EnhancedComponent;
};
Implementierung :
const mapActions = state => {
return {};
};
const mapState = state => {
return {
theme: (state && state.theme) || ""
};
};
export default connectContext(ThemeContext, mapState, mapActions)(Button);
Update: Letztendlich bin ich auf EventEmitter umgestiegen, um sich schnell ändernde, granulare Daten mit dynamischen Listenern (bei Mausbewegung) zu erhalten. Mir wurde klar, dass es das bessere Werkzeug für den Job war. Der Kontext eignet sich hervorragend für die allgemeine gemeinsame Nutzung von Daten, jedoch nicht bei hohen Aktualisierungsraten.
Kommt es nicht darauf an, den Kontext deklarativ zu abonnieren oder nicht? Bedingtes Umschließen einer Komponente in eine andere, die useContext() verwendet.
Die Hauptanforderung besteht darin, dass die innere Komponente keinen Zustand haben kann, da sie aufgrund der Verzweigung effektiv eine andere Instanz ist. Oder vielleicht können Sie die Komponente vorab rendern und dann cloneElement verwenden, um die Requisiten zu aktualisieren.
Einige Komponenten haben nichts mit dem Kontext beim Rendern zu tun, sondern müssen "nur einen Wert daraus lesen". Was vermisse ich?
Kann Context._currentValue sicher in der Produktion verwendet werden?
Ich versuche, Komponenten für Kontexte zu abonnieren, die möglichst billig zu rendern sind. Aber dann fand ich mich dazu, Requisiten wie auf die alte Art mit einem Sphagetti-Code zu übergeben oder Memos zu verwenden, um zu vermeiden, dass Updates abonniert werden, wenn mit Updates nichts logisches zu tun hat. Memo-Lösungen sind gut, wenn Ihr Komponenten-Rendering von Kontexten abhängt, aber ansonsten ...
@vamshi9666 hast du das
Ich fand https://recoiljs.org/ eine gute Lösung für dieses Problem. Ich fände es toll, wenn du es in React integrieren würdest.
@vamshi9666 hast du das
Ich habe es nur an einer Stelle verwendet und mein ursprüngliches Ziel ist es, die Zustandsverwaltung von jsx zu isolieren, aber auch kein Nicht-Boilerplate zu erstellen. Als ich die Mutationsfunktionalität erweitert habe, sah es dem Reducer- und Action-Creator-Muster von Redux sehr ähnlich. Was noch besser ist. Aber ich sehe keinen Sinn, etwas neu zu erfinden, wenn es tatsächlich schon da ist.
Hilfreichster Kommentar
Dies funktioniert wie geplant. Es gibt eine längere Diskussion darüber in https://github.com/facebook/react/issues/14110, wenn Sie neugierig sind.
Nehmen wir an, Sie haben aus irgendeinem Grund
AppContext
dessen Wert einetheme
Eigenschaft hat, und Sie möchten nur einigeExpensiveTree
beiappContextValue.theme
Änderungen neu rendern.TLDR bedeutet, dass Sie im Moment drei Optionen haben:
Option 1 (bevorzugt): Kontexte aufteilen, die sich nicht gemeinsam ändern
Wenn wir nur
appContextValue.theme
in vielen Komponenten benötigen, sich aberappContextValue
selbst zu oft ändert, könnten wirThemeContext
vonAppContext
aufteilen.Jetzt werden bei jeder Änderung von
AppContext
ThemeContext
Verbraucher nicht erneut gerendert.Dies ist die bevorzugte Lösung. Dann brauchen Sie kein spezielles Rettungspaket.
Option 2: Teilen Sie Ihre Komponente in zwei Teile, fügen Sie
memo
dazwischenWenn Sie Kontexte aus irgendeinem Grund nicht aufteilen können, können Sie das Rendering dennoch optimieren, indem Sie eine Komponente in zwei Teile aufteilen und spezifischere Requisiten an die innere übergeben. Sie würden immer noch das äußere rendern, aber es sollte billig sein, da es nichts tut.
Option 3: Eine Komponente mit
useMemo
darinSchließlich könnten wir unseren Code etwas ausführlicher gestalten, ihn aber in einer einzigen Komponente belassen, indem wir den Rückgabewert in
useMemo
umschließen und seine Abhängigkeiten angeben. Unsere Komponente würde immer noch erneut ausgeführt, aber React würde den untergeordneten Baum nicht erneut rendern, wenn alleuseMemo
Eingaben gleich sind.In Zukunft könnte es noch mehr Lösungen geben, aber die haben wir jetzt.
Beachten Sie jedoch, dass Option 1 vorzuziehen ist – wenn sich der Kontext zu oft ändert, sollten Sie ihn aufteilen .