Sådan konverteres withRouter til en React Hook

En af de største fordele ved React Hooks i den nye v16.7 er fjernelse af afhængighed af komponenter med højere orden. Mens jeg var i færd med at migrere mine klassestater til funktionelle kroge, var jeg svimmel med lejligheden til også at portere min med-router højereordens komponent til en krog. Jeg har brugt denne brugerdefinerede implementering til at gendanne mine komponenter igen ved ruteændring. react-routers indbyggede withRouter HOC gendanner ikke komponenter til ruteændring, og det er et vanskeligt problem at løse.

Denne artikel skal fungere som en tutorial til implementering af en React hook med pub-sub-funktionalitet. Pub-sub, forkortelse for "publish-subscribe", er en programmeringsmetodik, hvor et sæt abonnementsprocesser bliver underrettet, når en opdatering offentliggøres. Kort sagt, når placeringen ændres (den offentliggjorte begivenhed), vil jeg gendanne min komponent (lytterabonnementet).

Det endelige produkt kræver React Router v4.4 eller nyere, da tidligere versioner af React Router ikke afslørede router-konteksten, som vi bruger til at lytte til stedændringer. Hvis den seneste version af React Router ikke er tilgængelig for dig, kan du lytte til vinduet historiehistorie som et alternativ. Jeg valgte at bruge den samme “enkeltkilde af sandhed”, som reaktionsruteren bruger, og forsikrer, at mine tilstande altid er i synk.

Du kan bruge denne pakke selv via use-react-router på NPM eller bidrage til, gaffel eller på anden måde spionere på open source GitHub-depotet.

Brugeroplevelsen

Jeg starter hvert projekt med at stille spørgsmålet, "Hvad vil jeg gøre?" Dette betyder ikke, "Hvordan vil jeg implementere denne funktion?" Det betyder, som udvikler, hvordan ønsker jeg, at denne funktion allerede var implementeret . Hvad skal jeg gøre for at bruge denne funktion i fremtiden? Hvordan gør jeg det intuitivt og let at bruge?

Heldigvis er withRouter ikke en vanskelig implementering, så heller ikke dens krog.

Jeg vil ønske at importere en krog fra en standardeksporteksport.

Jeg vil kalde den kroge og være færdig med det.

Dette fralægger historik, placering og match rekvisitter fra min komponent og gør det muligt for mig at vælge og vælge, hvilket jeg vil have, der findes.

Jeg vil ikke være nødt til at implementere yderligere logik for at gengive igen ved lokalitetsændring. Jeg vil have, at denne funktionalitet allerede er implementeret i skarp kontrast til reaktor-routerens implementering af HOC. Det er bare personlig præference, og du er muligvis enig med deres implementering i stedet. Denne tutorial vil diskutere, hvordan man implementerer enten, da det er en almindelig anmodning om funktion, og det var meget relevant for min sag om personlig brug.

Implementeringen

De afhængigheder

For det første vores afhængigheder.

Vi har brug for routerkonteksten fra reaktor-router-pakken, da den indeholder de data, vi ønsker at få adgang til i vores komponent, og vi har brug for brugskontaktkroken fra reaktionspakken til "krog" til routerkonteksten. Da vi implementerer pub-sub-funktionalitet, har vi også brug for brug af Effect-krog indbygget i React. Dette giver os mulighed for at abonnere og afmelde sted fra stedændringerne.

Til sidst vil jeg importere useForceUpdate fra NPM-pakken med brugskraft-opdatering. Det er kun en kortfattet beskrivelse af at kalde useState-krogen til at tvinge en genudgivelse, hvilket er, hvad vi skal gøre, når placeringen ændres.

Krogen

Med vores afhængigheder importeret, kan vi begynde at skrive krogen

Vi begynder med at instantisere alle andre kroge, som vi har brug for. forceUpdate er en nu en funktion, der, når den kaldes, gengiver komponenten igen. routerContext er nu indholdet af reaktions-router-konteksten: et objekt med egenskaber for historie, placering og match - det samme, som du ville forvente at modtage som rekvisitter fra withRouter.

Hvis du ikke vil have gengivelsesfunktionaliteten, kan du stoppe her. Du kan fjerne forceUpdate-variablen, useEffect-import og use-force-update-afhængighed. Jeg vil rådgive at bruge en ekstern useReactRouter-krog ved at kalde useContext i din komponent udelukkende på grund af __RouterContext-navnet og @ næste semvar, der i øjeblikket er nødvendigt for at få adgang til React Router v4.4. Adgang til denne kontekst kan muligvis ændres, og at justeringen i den enkelte pakke er markant mindre arbejde end at foretage denne justering i hver routerafhængig komponent, du bruger i dit projekt. Det er også stadig så lidt mere intuitivt for udviklere at brugeReactRouter end useContext (__ RouterContext) - den ekstra kontekstimport er overflødig og uændret.

Pub-sub

For at implementere pub-sub-adfærd vil vi gerne brugeEffect. Dette tillader os at abonnere på komponentmontering og afmelde komponentafmontering. Teoretisk set vil det afmelde sig fra en gammel kontekst og abonnere på en ny, hvis routerkonteksten skulle ændres (en ønskelig opførsel, hvis det skulle ske), men der er ingen grund til at antage, at det nogensinde vil ske.

Udskift vores / * TODO * / med følgende:

useEffect tager en funktion, der udfører enhver montering og opdatering. Hvis denne funktion returnerer en funktion, vil den anden funktion udføre enhver afmontering og foropdatering.

Hvor effekt1 er den ydre funktion og virkning2 er den indre funktion, udføres komponentens livscyklus sådan:

mount> effekt1 ... effekt2> opdatering> effekt1 ... effekt2> afmonter

Den ydre funktion udføres umiddelbart efter en montering eller opdatering. Den indre funktion venter, indtil komponenten er ved at opdatere eller afmontere, før den udføres.

Vores mål er at abonnere på placeringsændringer, når vores komponent er monteret og afmelder stedændringer lige før vores komponent afmonteres.

Memoization-arrayet med useEffect siger “udfører ikke disse effektfunktioner under opdatering, medmindre denne matrix af parametre er ændret.” Vi kan bruge dette til ikke kontinuerligt at abonnere og afmelde stedændringer bare fordi komponenten er gengivet igen. Så længe routerkonteksten er den samme, behøver vi ikke at ændre vores abonnement. Derfor kan vores memoiseringsarray indeholde et enkelt element: [routerContext].

Hvordan abonnerer du på placeringsændringer? Ved at videregive en funktion til routerContext.history.listen, udføres denne funktion, hver gang routerhistorikken ændres. I dette tilfælde er funktionen, vi ønsker at udføre, simpelthen forceUpdate.

Og hvordan kan du afmelde dig lokationsændringer? Vi kan ikke bare lade dette abonnement eksistere efter, at komponenten har afmonteret - forceUpdate kaldes, men der er ikke en komponent til at opdatere!

routerContext.history.listen returnerer en afmeldingsfunktion, der, når den kaldes, fjerner abonnementslytteren (forceUpdate) fra begivenheden.

Ikke at der er nogen fordel ved dette, men hvis du vil gøre denne kode lidt kortere, kan du:

Og endnu kortere:

Hvor skal jeg hen herfra?

HOC-implementeringen af ​​withRouter, der leveres af react-router-pakken, trækker historik, placering og match fra komponentrekvisitter og giver dem højere prioritet end kontekst-API-værdierne. Dette skyldes sandsynligvis -komponenten, der leverer disse som rekvisitter, og matchens værdi, der behøver at komme fra rutens sti-fortolkning.

Selvom jeg endnu ikke har udnyttet dette i min pakke, tror jeg, at et solidt næste skridt ville være at bruge den tilsluttede komponents props som en parameter til useReactRouter, så den kan bruge den samme prop-prioritering.

Det er også værd at bemærke, at kroge er et eksperimentelt forslag. Denne pakke kan blive ændret, da kroge udvikler sig over tid. Du kan læse mere om dem i den officielle dokumentation.

Konklusion

Hvis du vil bidrage til denne open source-pakke eller se den i TypeScript, kan du stjerne den, gafle den, åbne problemer eller på anden måde tjekke den ud på GitHub. Du kan bruge denne pakke selv via brug-reager-router på NPM.

Hvis du kunne lide denne artikel, er du velkommen til at give den en klap eller to. Det er hurtigt, det er let, og det er gratis! Hvis du har spørgsmål eller relevant god rådgivning, skal du lade dem ligge i kommentarerne herunder.

For at læse flere af mine kolonner, kan du følge mig på LinkedIn og Twitter eller tjek min portefølje på CharlesStover.com.