200 Bytes für die Statusverwaltung von React-Komponenten
- Hooks reagieren : Das ist alles, was Sie brauchen, um den Status zu verwalten.
- ~ 200 Bytes , min + gz.
- Vertraute API : Verwenden Sie einfach Reagieren wie gewohnt.
- Minimale API : Fünf Minuten reichen aus, um es herauszufinden.
- In TypeScript geschrieben , um eine automatische Typinferenz bereitzustellen.
Die Hauptfrage ist: Ist dieses Paket besser als Redux? Nun ...
- Er ist weniger. Es ist 40 mal kleiner.
- Er ist schneller. Isolieren Sie Leistungsprobleme auf Komponentenebene.
- Es ist einfacher zu lernen. In jedem Fall müssen Sie in der Lage sein, React-Hooks und -Kontext zu verwenden, sie sind cool.
- Es ist einfacher zu integrieren. Verbinden Sie jeweils eine Komponente, ohne die Kompatibilität mit anderen React-Bibliotheken zu beeinträchtigen.
- Es ist einfacher zu testen. Das separate Testen von Reduzierern ist Zeitverschwendung und vereinfacht das Testen der React-Komponenten selbst.
- Es ist einfacher in Bezug auf die Eingabe. Es wurde geschrieben, um die Verwendung der Typinferenz zu maximieren.
- Er ist minimalistisch. Das ist nur Reagieren.
Codebeispiel
import React, { useState } from "react" import { createContainer } from "unstated-next" import { render } from "react-dom" function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment } } let Counter = createContainer(useCounter) function CounterDisplay() { let counter = Counter.useContainer() return ( <div> <button onClick={counter.decrement}>-</button> <span>{counter.count}</span> <button onClick={counter.increment}>+</button> </div> ) } function App() { return ( <Counter.Provider> <CounterDisplay /> <CounterDisplay /> </Counter.Provider> ) } render(<App />, document.getElementById("root"))
Einstellung zu Unstated
Ich (Jamie Kyle - ca. Per.) Betrachte diese Bibliothek als Nachfolger von Unstated . Ich habe Unstated gemacht, weil ich davon überzeugt war, dass React selbst eine großartige Arbeit bei der Verwaltung des Staates geleistet hat, und es fehlte nur ein einfacher Mechanismus, um den allgemeinen Zustand und die Logik zu trennen. Daher habe ich Unstated als "minimale" Lösung für dieses Problem erstellt.
Mit dem Aufkommen von Hooks ist React in Bezug auf die Hervorhebung des allgemeinen Zustands und der Logik viel besser geworden. So viel besser, dass Unstated aus meiner Sicht zu einer unnötigen Abstraktion geworden ist.
NICHT WENIGER , ich glaube, dass viele Entwickler wenig Ahnung haben, wie die Logik und der allgemeine Status der Anwendung mithilfe von React-Hooks getrennt werden können. Dies mag einfach auf eine unzureichende Qualität der Dokumentation und Trägheit der Community zurückzuführen sein, aber ich glaube, dass eine klare API diesen Mangel beheben kann.
Unstated Next ist genau diese API. Anstatt die "Mindest-API für die Freigabe von Status und Logik in React" zu sein, verfügt sie jetzt über die "Mindest-API für das Verständnis der Freigabe von Status und Logik in React".
Ich mag React wirklich, ich möchte, dass React gedeiht. Ich würde es vorziehen, wenn die Community die Verwendung externer Bibliotheken für die Verwaltung von Status wie Redux aufgibt und endlich damit beginnt, die in React integrierten Tools in vollem Umfang zu nutzen.
Wenn Sie anstelle von Unstated nur React verwenden, werde ich dies begrüßen. Schreiben Sie darüber in Ihren Blogs! Sprechen Sie auf Konferenzen darüber! Teilen Sie Ihr Wissen mit der Community.
Unstated-next Guide
Wenn Sie mit React Hooks noch nicht vertraut sind, empfehle ich Ihnen, mit dem Lesen aufzuhören und zu lesen
Hervorragende Dokumentation auf der React-Website .
Mit Hilfe von Hooks können Sie also so etwas wie diese Komponente schreiben:
function CounterDisplay() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return ( <div> <button onClick={decrement}>-</button> <p>You clicked {count} times</p> <button onClick={increment}>+</button> </div> ) }
Wenn die Komponentenlogik an mehreren Stellen verwendet werden muss, kann sie herausgenommen werden
in einen separaten benutzerdefinierten Haken:
function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment } } function CounterDisplay() { let counter = useCounter() return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> </div> ) }
Aber was tun, wenn Sie eine allgemeine Bedingung und nicht nur Logik benötigen?
Der Kontext ist hier nützlich:
function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment } } let Counter = createContext(null) function CounterDisplay() { let counter = useContext(Counter) return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> </div> ) } function App() { let counter = useCounter() return ( <Counter.Provider value={counter}> <CounterDisplay /> <CounterDisplay /> </Counter.Provider> ) }
Es ist wunderbar und wunderbar; Je mehr Leute in diesem Stil schreiben, desto besser.
Es lohnt sich jedoch, etwas mehr Struktur und Klarheit hinzuzufügen, damit die API Ihre Absichten sehr klar zum Ausdruck bringt.
Zu diesem createContainer()
wir die Funktion createContainer()
hinzugefügt, damit Sie Ihre benutzerdefinierten Hooks als "Container" behandeln können, damit unsere klare und präzise API nicht falsch verwendet werden kann.
import { createContainer } from "unstated-next" function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment } } let Counter = createContainer(useCounter) function CounterDisplay() { let counter = Counter.useContainer() return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> </div> ) } function App() { return ( <Counter.Provider> <CounterDisplay /> <CounterDisplay /> </Counter.Provider> ) }
Vergleichen Sie den Komponententext vor und nach unseren Änderungen:
- import { createContext, useContext } from "react" + import { createContainer } from "unstated-next" function useCounter() { ... } - let Counter = createContext(null) + let Counter = createContainer(useCounter) function CounterDisplay() { - let counter = useContext(Counter) + let counter = Counter.useContainer() return ( <div> ... </div> ) } function App() { - let counter = useCounter() return ( - <Counter.Provider value={counter}> + <Counter.Provider> <CounterDisplay /> <CounterDisplay /> </Counter.Provider> ) }
Wenn Sie in TypeScript schreiben (und wenn nicht, empfehle ich Ihnen dringend, sich damit vertraut zu machen), erhalten Sie auch eine bessere Typinferenz. Wenn Ihr benutzerdefinierter Hook stark typisiert ist, funktioniert die Ausgabe aller anderen Typen automatisch.
API
createContainer(useHook)
import { createContainer } from "unstated-next" function useCustomHook() { let [value, setValue] = useState() let onChange = e => setValue(e.currentTarget.value) return { value, onChange } } let Container = createContainer(useCustomHook)
<Container.Provider>
function ParentComponent() { return ( <Container.Provider> <ChildComponent /> </Container.Provider> ) }
Container.useContainer()
function ChildComponent() { let input = Container.useContainer() return <input value={input.value} onChange={input.onChange} /> }
useContainer(Container)
import { useContainer } from "unstated-next" function ChildComponent() { let input = useContainer(Container) return <input value={input.value} onChange={input.onChange} /> }
Tipps
Tipp 1: Zusammenführen von Containern
Da es sich um benutzerdefinierte Haken handelt, können wir Container in anderen Haken kombinieren.
function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment, setCount } } let Counter = createContainer(useCounter) function useResettableCounter() { let counter = Counter.useContainer() let reset = () => counter.setCount(0) return { ...counter, reset } }
Tipp 2: Verwenden Sie kleine Behälter
Container werden am besten klein gemacht und sind klar auf eine bestimmte Aufgabe ausgerichtet. Wenn Sie zusätzliche Geschäftslogik in Containern benötigen, führen Sie neue Vorgänge in separaten Hooks aus und lassen Sie den Status in Containern speichern.
function useCount() { return useState(0) } let Count = createContainer(useCount) function useCounter() { let [count, setCount] = Count.useContainer() let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) let reset = () => setCount(0) return { count, decrement, increment, reset } }
Tipp 3: Komponentenoptimierung
Es gibt keine separate „Optimierung“ für nicht angegebene unstated-next
, die üblichen Methoden zur Optimierung von React-Komponenten reichen aus.
1) Optimierung schwerer Teilbäume durch Aufteilen von Komponenten.
An:
function CounterDisplay() { let counter = Counter.useContainer() return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> <div> <div> <div> <div> </div> </div> </div> </div> </div> ) }
Nachher:
function ExpensiveComponent() { return ( <div> <div> <div> <div> </div> </div> </div> </div> ) } function CounterDisplay() { let counter = Counter.useContainer() return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> <ExpensiveComponent /> </div> ) }
2) Optimierung schwerer Operationen mit dem useMemo () - Hook
An:
function CounterDisplay(props) { let counter = Counter.useContainer()
Nachher:
function CounterDisplay(props) { let counter = Counter.useContainer()
3) Reduzieren Sie die Anzahl der Renderings mit React.memo () und useCallback ().
An:
function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment } } let Counter = createContainer(useCounter) function CounterDisplay(props) { let counter = Counter.useContainer() return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> </div> ) }
Nachher:
function useCounter() { let [count, setCount] = useState(0) let decrement = useCallback(() => setCount(count - 1), [count]) let increment = useCallback(() => setCount(count + 1), [count]) return { count, decrement, increment } } let Counter = createContainer(useCounter) let CounterDisplayInner = React.memo(props => { return ( <div> <button onClick={props.decrement}>-</button> <p>You clicked {props.count} times</p> <button onClick={props.increment}>+</button> </div> ) }) function CounterDisplay(props) { let counter = Counter.useContainer() return <CounterDisplayInner {...counter} /> }
Migration mit nicht angegeben
Ich veröffentliche diese Bibliothek absichtlich als separates Paket, da die gesamte API völlig neu ist. Daher können Sie beide Pakete parallel installieren und schrittweise migrieren.
Teilen Sie Ihre Eindrücke vom Übergang zum nicht angegebenen unstated-next
, da ich in den nächsten Monaten zwei Dinge auf der Grundlage dieser Informationen planen werde:
- Stellen Sie sicher, dass
unstated-next
alle Anforderungen nicht angegebener Benutzer erfüllt. - Stellen Sie sicher, dass es für nicht angegebene einen klaren und präzisen Prozess für die Migration zu nicht angegebenen
unstated-next
.
Vielleicht werde ich der alten oder neuen Bibliothek einige APIs hinzufügen, um Entwicklern das Leben zu erleichtern. Ich verspreche, dass die hinzugefügten APIs so minimal wie möglich sind und ich werde mein Bestes tun, um die Bibliothek klein zu halten.
In Zukunft werde ich wahrscheinlich den nicht angegebenen unstated-next
Code als neue Hauptversion zurück auf nicht angegeben portieren. unstated-next
ist weiterhin verfügbar, sodass Sie unstated@2
und unstated-next
im selben Projekt parallel verwenden können. Wenn Sie die Migration abgeschlossen haben, können Sie ein Upgrade auf unstated@3
und unstated-next
löschen (natürlich alle Importe aktualisieren ... es sollte genügend Suchen und Ersetzen geben).
Trotz der dramatischen Änderung in der API hoffe ich, dass ich Ihnen die einfachste Migration bieten kann. Ich würde mich über Kommentare freuen, was besser gemacht werden könnte.
Referenzen