Heim >Web-Frontend >js-Tutorial >Reagieren: Wiederverwendbar, entkoppelt und isoliert
Wenn Sie an einer neuen Seite arbeiten, beginnen Sie nicht einfach damit, Komponenten auf der Seite selbst zu schreiben. Beginnen Sie mit der Individualisierung von Komponenteneinheiten, damit jede unabhängig genug ist und Änderungen am globalen Status kein erneutes Rendern verursachen. Stellen Sie sich zum Beispiel vor, Sie hätten die Aufgabe, diese Seite zu erstellen.
Sie könnten wie ich, als ich das Programmieren lernte, versucht sein, alle diese Komponenten in eine einzige Datei zu schreiben.
// WRONG export const LoginPage = () => { const [userId, setUserId] = useState('') const [password, setPassword] = useState('') return ( <div> <div className="d-flex flex-row"> <div id="marketing-container"> <svg {...fbLogo} /> <h2>Connect with friends and the world around you on Facebook</h2> </div> <div id="login-form"> <div> <input id="user-id" value={userId} onChange={(e) => setUserId(e.target.value)} /> <input id="password" value={password} onChange={(e) => setPassword(e.target.value)} /> <button>Log in</button> <a href="/forgot-password">Forgot password</a> <br /> <button>Create new account</button> </div> <div> <strong>Create a page</strong> <span>for a celebrity, brand or business.</span> </div> </div> </div> </div> ) } export default LoginPage
Aber die Realität ist, dass viele dieser Elemente in Zukunft in der gesamten App wiederverwendet werden könnten und das bedeutet, dass sie neu geschrieben oder kopiert/eingefügt werden müssten. Wenn Sie mit einem klar definierten Design arbeiten, ist es wahrscheinlich, dass Elemente wie Legos verwendet werden. Das auf der Anmeldeseite angezeigte Logo ist dasselbe wie das auf dem Dashboard-Bildschirm, vielleicht hat es natürlich nur eine andere Größe. Das Gleiche gilt für die Benutzer-ID-Eingabe. Vom Design her wäre sie wahrscheinlich dieselbe wie die auf der Benutzerbearbeitungsseite.
Was mich zu meinem nächsten Punkt bringt: Komponenten sollten von der Geschäftslogik zur Präsentationslogik entkoppelt werden. Damit meine ich, dass der Teil, der mit dem Staat kommuniziert, eine eigene Komponente sein sollte und diese Komponente lediglich Präsentations-Requisiten an die Präsentationskomponente weitergibt.
// Presentational component const UserIdInputComponent = ({ value, onChange }) => <input value={value} onChange={onChange} /> // Logic component export const UserIdInput = () => { const [value, setValue] = useState('') const handleChange = (e) => setValue(e.target.value) return <UserIdInputComponent value={value} onChange={handleChange} /> }
Dadurch können Tools wie das Storybook ordnungsgemäß funktionieren, indem nur Präsentationskomponenten exportiert werden, die von der Statusverwaltung entkoppelt sind. Es kann lästig sein, eine logiklastige Komponente in Storybook integrieren zu müssen, die API-Aufrufe durchführt und globale Zustandsänderungen vornimmt. Mit diesem Ansatz können Sie sehen, wie sich eine Komponente anhand verschiedener Requisiten optisch verändert.
Zurück zur Hauptseite. Sie können wahrscheinlich sehen, wohin ich damit will. Anstatt alles auf die gleiche Seite zu schreiben. Überlegen Sie, wie diese Komponente wiederverwendet werden kann, wie sie vom Zustand entkoppelt werden kann und wie sie isoliert werden kann, sodass sie nie wieder gerendert wird, es sei denn, die mit dieser Komponente verbundenen Requisiten ändern sich.
export const LoginPage = () => ( <div> <div className="d-flex flex-row"> <FbMarketing /> <LoginForm /> </div> </div> ) export default LoginPage
Im besten Fall beginnen Sie so mit dem Codieren. Es wäre umständlicher, es noch einmal zu überarbeiten, sobald Sie sehen, dass alles wie erwartet funktioniert. Ich möchte schnell etwas auf dem Bildschirm sehen, die Angst beruhigen und von Anfang an mit dem Aufbau der richtigen Struktur beginnen.
const FbLogo = () => ( <svg {...fbLogoAttributes} /> ) const FbSlogan = () => ( <h2>Connect with friends and the world around you on Facebook.</h2> ) const FbMarketing = () => ( <> <FbLogo /> <FbSlogan /> </> )
Hier finden Sie zum jetzigen Zeitpunkt alle Präsentationen. Diese können weiter individualisiert werden als FbLogoSmall, FbLogoMedium usw.
Jetzt kommt der Teil, der etwas Logik enthält, das Anmeldeformular. Ich bin mir nicht sicher, ob es „Anmelden“, „Anmelden“ oder „Anmelden“ heißt, aber wir werden weiterhin die Facebook-Terminologie „Anmelden“ verwenden.
Zur Erinnerung: Jede Komponente sollte wiederverwendbar, entkoppelt und isoliert sein.
Wiederverwendbar:
Machen wir zunächst den UserIdInput wiederverwendbar und kopieren Sie diesen Ansatz dann auf die andere Passworteingabe: Es ist erwähnenswert, dass Eingaben auf Produktionsebene andere Attribute wie Test-ID, Klassen, die sich basierend auf Requisiten ändern, Aria-Attribute, Autofokus usw. enthalten Weitere andere Requisiten/Attribute, abhängig von den Tools, die die Codebasis verwendet. Wenn Ihnen jemand sagt, dass es komplexer ist als das, was ich hier schreibe, hören Sie dieser Person zu.
// UserIdInput.js import { useContext, createContext } from "react"; export const UserIdContext = createContext(); const UserIdInput = () => { const { userId, setUserId } = useContext(UserIdContext); const handleChange = (e) => { setUserId(e.target.value); }; return <UserIdInputComponent value={userId} onChange={handleChange} />; };
Jetzt kann diese Eingabe beispielsweise im Benutzerbearbeitungsformular wiederverwendet werden. So würde die Passworteingabe aussehen:
// PasswordInput.js import { useContext, createContext } from "react"; export const PasswordContext = createContext(); const PasswordInput = () => { const { password, setPassword } = useContext(PasswordContext); const handleChange = (e) => { setPassword(e.target.value); }; return ( <div> <PasswordInputComponent value={password} onChange={handleChange} /> </div> ); };
Entkoppelt:
Entkoppelt im Sinne der Entkopplung der Logik vom Präsentationsteil, „Geschäftslogik“ vs. visuell: Hier können wir sehen, dass Requisiten ohne Änderungen oder neue Funktionsdefinitionen in der Mitte übergeben werden, zum Teufel gebe ich sogar die jsx zurück direkt nach oben ohne Return-Schlüsselwort. Nochmals: Wenn Ihnen jemand sagt, dass es komplizierter ist, dann ist es … Beschriftungen sollten ihre eigenen Komponenten sein, Eingaben auch.
// UserIdInputComponent.js const UserIdInputComponent = ({ value, onChange }) => ( <div> <label>User Id:</label> <input type="text" value={value} onChange={onChange} required /> </div> );
// PasswordInputComponent.js const PasswordInputComponent = ({ value, onChange }) => ( <div> <label>Password:</label> <input type="password" value={value} onChange={onChange} required /> </div> );
Isoliert:
Wir haben uns bereits um den isolierten Teil gekümmert, indem wir einen Kontext erstellt haben. Wenn wir nun eine der Eingaben ändern, wird die andere Eingabe nicht erneut gerendert. Die einzigen Elemente, die neu gerendert werden, sind die geänderte Eingabe und die Schaltfläche „Anmelden“. Dies ist ein guter Indikator dafür, ob Ihre React-App richtig optimiert ist. Eine vorzeitige Optimierung ist manchmal gut. Stärkt das Team weiter.
const LoginButton = () => { const { userId } = useContext(UserIdContext); const { password } = useContext(PasswordContext); const onClick = (e) => { e.preventDefault(); console.log("form submit", userId, password) }; return <button onClick={onClick}>Log in</button>; };
Außer! Das ist eigentlich nicht passiert, ich habe versucht, die Änderungen mithilfe des Kontexts zu isolieren, aber als es darum ging, Benutzer-ID und Passwort zu teilen, musste ich Redux verwenden, denn sobald ich UserIdProvider zum Umschließen des LoginButtons verwendet habe, wurde ein neuer Status mit neuer Benutzer-ID und neuem Passwort erstellt . So sieht es mit Redux aus.
// LoginButton.js import { useSelector } from "react-redux"; const LoginButton = () => { const { userId, password } = useSelector(state => state) const onClick = (e) => { e.preventDefault(); console.log("form submit", userId, password); }; return <button onClick={onClick}>Log in</button>; }; export default LoginButton
Hätte es wahrscheinlich schon einmal tippen sollen, aber hier ist der Redux-Store.
// store.js import { createSlice, configureStore } from '@reduxjs/toolkit' const login = createSlice({ name: 'login', initialState: { userId: '', password: '', }, reducers: { userId: (state, action) => { state.userId = action.payload }, password: (state, action) => { state.password = action.payload } } }) export const { userId: setUserId, password: setPassword } = login.actions export const store = configureStore({ reducer: login.reducer })
Ich kenne mich mit Redux aus, aber es funktioniert einfach wunderbar, Änderungen zu isolieren, sodass ein erneutes Rendern minimiert wird. Normalerweise würde ich niemandem trauen, der Re-Renderings um jeden Preis vermeidet, aber das ist nur ein guter Hinweis auf guten Reaktionscode.
Here are the updated files for the two inputs. Not a lot changed but pay attention to how easy it was for me to change only the business logic component. Changed the value selector, the handleChange function and that was it. This is one of the advantages of decoupling, it’s not that obvious with such a small component but a codebase that uses complex logic I can see how this approach can be beneficial.
// UserIdInput.js (revised final) import { setUserId } from "./store"; import { useDispatch, useSelector } from "react-redux"; const UserIdInputComponent = ({ value, onChange }) => ( <div> <label>User Id:</label> <input type="text" value={value} onChange={onChange} required /> </div> ); const UserIdInput = () => { const userId = useSelector(({ userId }) => userId) const dispatch = useDispatch() const handleChange = (e) => { dispatch(setUserId(e.target.value)) }; return <UserIdInputComponent value={userId} onChange={handleChange} />; };
// PasswordInput.js (revised final) import { useDispatch, useSelector } from "react-redux"; import { setPassword } from "./store"; const PasswordInputComponent = ({ value, onChange }) => ( <> <label>Password:</label> <input type="password" value={value} onChange={onChange} required /> </> ); const PasswordInput = () => { const password = useSelector(({ password }) => password) const dispatch = useDispatch() const handleChange = e => { dispatch(setPassword(e.target.value)) }; return <PasswordInputComponent value={password} onChange={handleChange} /> };
The result should only highlight updates on the changed input and the login button itself like so:
There’s a problem though, the labels are also updating. Let’s fix that really quick just to prove the point of over, but potentially necessary optimization. Up to your discretion.
// UserIdInput.js import { setUserId } from "./store"; import { useDispatch, useSelector } from "react-redux"; const UserIdInputComponent = ({ value, onChange }) => ( <input type="text" value={value} onChange={onChange} required /> ); const UserIdInput = () => { const userId = useSelector(({ userId }) => userId) const dispatch = useDispatch() const handleChange = (e) => { dispatch(setUserId(e.target.value)) }; return <UserIdInputComponent value={userId} onChange={handleChange} />; }; // separated the label from the logic heavy component export const UserIdInputWithLabel = () => ( <div> <label>User id: </label> <UserIdInput /> </div> ) export default UserIdInputWithLabel
Here is the password input.
// PasswordInput.js import { useDispatch, useSelector } from "react-redux"; import { setPassword } from "./store"; const PasswordInputComponent = ({ value, onChange }) => ( <input type="password" value={value} onChange={onChange} required /> ); const PasswordInput = () => { const password = useSelector(({ password }) => password) const dispatch = useDispatch() const handleChange = e => { dispatch(setPassword(e.target.value)) }; return <PasswordInputComponent value={password} onChange={handleChange} /> }; // separated label from logic heavy component const PasswordInputWithLabel = () => ( <div> <label>Password: </label> <PasswordInput /> </div> ) export default PasswordInputWithLabel
This approach yields the following results:
Fully optimized.
Available here: https://github.com/redpanda-bit/reusable-decoupled-isolated
There you have it, reusable, decoupled, and isolated react components. Very small example but hope that it gives you an idea of how production grade react applications look like. It may be disappointing for some to see all the work that goes into creating a good react component, but I’ll tell ya, once you are faced with a huge form that has complex elements and possibly some animation you will see positive gains on speed. The last thing you want is an input lagging behind in front of a 100 words per minute types.
https://nextjs.org/
https://redux.js.org/
Das obige ist der detaillierte Inhalt vonReagieren: Wiederverwendbar, entkoppelt und isoliert. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!