Lorsque vous travaillez sur une nouvelle page, ne commencez pas simplement à écrire des composants sur la page elle-même. Commencez à individualiser les unités de composants afin que chacun soit suffisamment indépendant et que les modifications de l'état global ne provoquent pas de nouveau rendu. Par exemple, imaginez que l'on vous confie la tâche de créer cette page.
Vous pourriez être tenté, comme je l'étais lorsque j'apprenais à coder, d'écrire tous ces composants dans un seul fichier.
// 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
Mais la réalité est que beaucoup de ces éléments pourraient être réutilisés dans l'application à l'avenir et cela signifie qu'ils devront être réécrits ou copiés/collés. Lorsque vous travaillez avec un design bien défini, il est probable que les éléments soient utilisés comme des legos, le logo affiché sur la page de connexion sera le même que celui sur l'écran du tableau de bord, peut-être juste une taille différente bien sûr. Idem avec la saisie de l'identifiant utilisateur, du point de vue de la conception, ce serait probablement le même que celui de la page de modification de l'utilisateur.
Ce qui m'amène au point suivant, les composants doivent être découplés de la logique métier à la logique de présentation. Ce que je veux dire par là, c'est que la partie qui communique avec l'État devrait être son propre composant et ce composant transmettrait simplement des accessoires de présentation au composant de présentation.
// 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} /> }
Cela permet à des outils tels que le livre d'histoires de fonctionner correctement en exportant uniquement le composant de présentation découplé de la gestion de l'état. Il peut être ennuyeux de devoir intégrer un composant logique dans le livre d'histoires, un composant qui effectue des appels d'API et modifie l'état global. Avec cette approche, vous pouvez voir comment un composant changera visuellement en fonction de différents accessoires.
Retour à la page principale. Vous pouvez probablement voir où je veux en venir. Au lieu de tout écrire sur la même page. Pensez à la manière dont ce composant peut être réutilisé, à la manière dont il peut être découplé de l'état et à la manière dont il peut être isolé afin qu'il ne soit jamais restitué à moins que les accessoires liés à ce composant ne changent.
export const LoginPage = () => ( <div> <div className="d-flex flex-row"> <FbMarketing /> <LoginForm /> </div> </div> ) export default LoginPage
Dans le meilleur des cas, voici comment commencer à le coder. Il serait plus fastidieux de revenir et de le refactoriser une fois que vous aurez constaté que tout fonctionne comme prévu. Je suis coupable de vouloir voir quelque chose de rapide à l'écran, calmer l'anxiété et commencer à construire la bonne structure dès le début.
const FbLogo = () => ( <svg {...fbLogoAttributes} /> ) const FbSlogan = () => ( <h2>Connect with friends and the world around you on Facebook.</h2> ) const FbMarketing = () => ( <> <FbLogo /> <FbSlogan /> </> )
Voici toute la présentation à ce stade. Ceux-ci peuvent être davantage individualisés en tant que FbLogoSmall, FbLogoMedium, etc.
Maintenant, la partie qui contient un peu de logique, le formulaire de connexion. Je ne sais pas s'il s'agit de connexion, de connexion ou de connexion, mais nous allons continuer et utiliser la terminologie de Facebook, « se connecter ».
Pour rappel, chaque composant doit être réutilisable, découplé et isolé.
Réutilisable :
Rendons d'abord le UserIdInput réutilisable, puis copions cette approche sur l'autre entrée de mot de passe : il convient de noter que les entrées au niveau de la production incluront d'autres attributs tels que l'identifiant de test, les classes qui changent en fonction des accessoires, les attributs aria, la mise au point automatique, etc. plus d'autres accessoires/attributs en fonction des outils utilisés par la base de code. Si quelqu'un vous dit que c'est plus complexe que ce que j'écris ici, écoutez cette personne.
// 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} />; };
Désormais, cette saisie peut être réutilisée sur le formulaire d'édition utilisateur par exemple. Voici à quoi ressemblerait la saisie du mot de passe :
// 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> ); };
Découplé :
Découplé dans le sens de dissocier sa logique de sa partie présentation, « logique métier » vs visuel : ici, nous pouvons voir que les accessoires sont passés sans modifications ni nouvelles définitions de fonction au milieu, bon sang, je renvoie même le jsx directement sans mot-clé de retour. Encore une fois, si quelqu'un vous dit que c'est plus compliqué que cela, c'est que… les étiquettes devraient être leurs propres composants, les entrées également.
// 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> );
Isolé :
Nous avons déjà pris soin de la partie isolée en créant un contexte, désormais, chaque fois que nous modifions l'une des entrées, l'autre entrée ne serait pas restituée. Le seul élément qui sera restitué est l'entrée en cours de modification et le bouton de connexion. C'est un bon indicateur pour savoir si votre application React est correctement optimisée, une optimisation prématurée est parfois bonne. Améliore les compétences de l'équipe.
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>; };
Sauf ! Cela ne s'est pas produit, j'ai essayé d'utiliser le contexte pour isoler les modifications, mais lorsqu'il s'agissait de partager l'ID utilisateur et le mot de passe, j'ai dû utiliser Redux car dès que j'ai utilisé UserIdProvider pour envelopper le LoginButton, il a créé un nouvel état avec un nouvel ID utilisateur et un nouveau mot de passe. . Voilà à quoi ça ressemble avec redux.
// 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
J'aurais probablement dû le taper avant, mais voici le magasin redux.
// 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 })
Je date avec Redux mais cela fonctionne à merveille pour isoler les changements afin de minimiser les nouveaux rendus. Habituellement, je ne ferais pas vraiment confiance à quelqu'un qui évite à tout prix les nouveaux rendus, mais c'est juste une bonne indication d'un bon code de réaction.
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/
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!