Cet article est écrit par la colonne tutorielle composer pour vous présenter comment encapsuler un React Context Composer étape par étape. J'espère qu'il sera utile aux amis qui en ont besoin !
Comment encapsuler un React Context Composer étape par étape ?
Motivation
React propose de nombreuses solutions de gestion d'état, telles que Redux, Mobx, Recoil, etc. Jusqu'à présent, je n'ai expérimenté que Redux, et je pense que c'est encore un peu encombrant. Parce que j'écris habituellement beaucoup de Hooks, je préfère utiliser Context Provider avec le hook useContext, ce qui facilite la division et la combinaison d'états. Ici, nous ne discuterons pas des avantages et des inconvénients de chaque solution de gestion d’état, mais nous concentrerons sur un problème d’imbrication multicouche rencontré lors de l’utilisation de Context.
L'image ci-dessous est un code extrait d'un projet taro + react hooks + ts que j'écrivais récemment. J'ai divisé un état global (le but du fractionnement est de réduire le rendu inutile), puis je les ai imbriqués. Cette façon d’écrire me rappelle le sentiment d’être dominé par l’enfer du rappel, ce qui est très inconfortable. J'ai donc pensé à sceller moi-même un composant d'ordre élevé et à « aplatir » la structure en termes d'écriture.
<LoadingContext.Provider value={{ loading, setLoading }}> <UserDataContext.Provider value={{ name: "ascodelife", age: 25 }}> <ThemeContext.Provider value={"light"}> {/* ....more Providers as long as you want */} </ThemeContext.Provider> </UserDataContext.Provider> </LoadingContext.Provider>
La solution la plus simple
Ici, j'ai rapidement écrit la première solution, en utilisant réduireRight pour terminer l'imbrication du fournisseur.
La raison pour laquelle réduireRight est utilisé ici au lieu de réduire est que nous sommes plus habitués à l'ordre d'écriture de la couche externe à la couche interne.
// ContextComposer.tsx import React from 'react'; type IContextComposerProps = { contexts: { context: React.Context<any>; value: any }[]; }; const ContextComposer: React.FC<IContextComposerProps> = ({ contexts, children }) => { return ( <> {contexts.reduceRight((child, parent) => { const { context, value } = parent; return <context.Provider value={value}>{child}</context.Provider>; }, children)} </> ); }; export default ContextComposer; // App.tsx <ContextComposer contexts={[ { context: ThemeContext, value: "light" }, { context: UserDataContext, value: { name: "ascodelife", age: 25 } }, { context: LoadingContext, value: { loading, setLoading } }, ]}> { children } </ContextComposer>
Après une expérience réelle, j'ai découvert que même si cela peut être utilisé, l'expérience de développement est un peu pire. Le problème est que la valeur transmise lorsque le composant entre dans le paramètre est de type any, ce qui signifie que la vérification de type statique de ts est abandonnée. Lors du passage des paramètres, étant donné que la vérification du type statique ne sera pas effectuée sur la valeur, non seulement il n'y aura pas d'invite de code lors de la saisie du code, mais cela peut également provoquer des erreurs d'exécution de niveau relativement faible. Mauvaise critique !
Un plan de transformation basé sur React.cloneElement()
Afin de transformer la solution ci-dessus, je suis tombé sur une fonction relativement impopulaire mais facile à utiliser - React.cloneElement(). Il n'y a pas beaucoup de points à noter à propos de cette fonction. Jetez principalement un œil à ses trois paramètres d'entrée. Le premier est l'élément parent, le second les accessoires parents et le troisième les paramètres restants... les enfants, à l'exception des éléments parents. premier paramètre. À part cela, toutes les autres valeurs sont facultatives.
Par exemple :
<!-- 调用函数 --> React.cloneElement(<div/>,{},<span/>); <!-- 相当于创建了这样一个结构 --> <div> <span></span> </div>
Ensuite, commençons la transformation. Le cadre réduireRight reste inchangé. Modifiez le type de paramètre d'entrée et le rappel réduireRight.
// ContextComposer.tsx import React from 'react'; type IContextComposerProps = { contexts: React.ReactElement[]; }; const ContextComposer: React.FC<IContextComposerProps> = ({ contexts, children }) => { return ( <> {contexts.reduceRight((child, parent) => { return React.cloneElement(parent,{},child); }, children)} </> ); }; export default ContextComposer; // App.tsx <ContextComposer contexts={[ <ThemeContext.Provider value={"light"} />, <UserDataContext.Provider value={{ name: "ascodelife", age: 25 }} />, <LoadingContext.Provider value={{ loading, setLoading }} />, ]}> { children } </ContextComposer>
Après la transformation, lorsque nous passons des paramètres, il semble que nous créons réellement un composant (bien sûr, le composant est réellement créé, mais le composant lui-même n'est pas rendu au Dom virtuel, et ce qui est réellement rendu est après en cours de copie). Dans le même temps, le problème de vérification de type statique de valeur sur lequel nous venons de nous concentrer a également été résolu.
astuces : React.cloneElement(parent,{},child) est équivalent à React.cloneElement(parent,{children:child}), savez-vous pourquoi ?
Ressources associées
Le code source a été synchronisé avec github (https://github.com/ascodelife/react-context-provider-composer).
Il a également été emballé dans l'entrepôt npm (https://www.npmjs.com/package/@ascodelife/react-context-provider-composer), bienvenue pour en faire l'expérience.
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!