Bygga en Instagram-klon i React med GraphQL och Hasura - Del II

Denna handledning är skriven av Abhijeet Singh och publicerades som en del av Hasura Technical Writer Program - ett initiativ som stöder författare som skriver guider och tutorials för den öppna källan Hasura GraphQL Engine.

I del 1 av denna serie installerar vi vår backend och Auth0. I den här delen installerar vi vår React-app och ansluter den till vår backend.

React App Setup

Vi kommer först att implementera användarautentisering. Vi kommer att använda JWT (JSON webbtokens) för autentisering. Låt oss först skapa en grundläggande rubrik i vår reaktionsapp för att visa inloggningsknappen.

Byt ut innehållet i stilar / App.css-filen med den här filen. Dessa stilar kommer att användas i hela vår app så att du inte behöver oroa dig för stylingen. Hämta också den här filen och placera den i dina stilar / kataloger. Vi kommer att använda detta för att visa olika knappar i vår app.

Installera Apollo GraphQL Client

Byt ut innehållet i App.js för att använda Apollo GraphQL-klienten som visas nedan. (Se apollo github-arkivet för mer hjälp)

I rad 15 ändrar du uri till din GraphQL Endpoint på hasura, som du kan hitta på hasura-konsolen (kom ihåg var du skapade tabeller). Här har vi importerat rubrikkomponenten som vi kommer att implementera nu.

Skapa sidhuvudkomponent och använd reageringsvägar:

Vi kommer att använda reaktor-router för att implementera applikationsbeteende på en sida. Installera rea-router med:

$ npm installera react-router-dom
React Router och dynamisk routing på klientsidan gör det möjligt för oss att bygga en webbsapplikning med en sida med navigering utan att sidan uppdateras när användaren navigerar. React Router använder komponentstruktur för att ringa komponenter, som visar rätt information.
Genom att förhindra en siduppdatering och använda router eller länk förhindras blixt på en vit skärm eller en blank sida. Detta är ett allt vanligare sätt att få en mer sömlös användarupplevelse. (källa)

För att använda reaktor-router i vår app, måste vi lägga in hela appen i BrowserRouter. Det är en kontextleverantör för routing, som tillhandahåller flera rekvisita som krävs för routing (som matchning, plats, historik). Se detta om du inte känner till kontext. Byt ut innehållet i index.js:

Därefter skapar vi en Header-komponent för navigering i appen. Skapa en Header.js-fil i komponentkatalogen. Innehållet i Header.js ska vara:

Här skapar vi en navbar som liknar Instagram navbar. Senare lägger vi till några rutter för att navigera. Det är allt! Vi har framgångsrikt skapat en rubrik Navbar och använt reageringsvägar i vår app.

Just nu ska din app se ut så här

Auth0 JWT-integration med React App

Följ tillsammans med Auth0-react-quickstart-guide som referens för att inkludera Auth0 i react-appen. Konfigurera Auth0-klienten genom att ställa in tillåtna återuppringningsadresser, tillåtna webgreningar, tillåtna utloggningsadresser på http: // localhost: 3000 och lägg till det anpassade API om du inte redan har gjort det. Installera nu auth0-spa-js:

$ npm installera @ autor0 / auth0-spa-js

Nu kommer vi att inkludera react-authent0-wrapper i vår app, som är en uppsättning av anpassade react-hooks som gör att du kan arbeta med Auth0 SDK. Skapa en ny katalog src / auth och lägg till filen react-auth0-wrapper.js fylla i den med kod härifrån.

Lägg nu till en annan fil som auth / auth_config.json i src / auth. Bevilja autor_config.json med följande kod (ändra värdena därefter):

Nu är vi redo att inkludera inloggningsfunktionalitet i vår reaktionsapp. I grunden kommer vi att inkludera en inloggningsknapp i rubriken. Den här knappen leder till inloggning via Auth0 med omdirigering till vår localhost när inloggningen / registreringen är klar. Samtidigt uppdateras inloggnings- / anmälningsdata i vår användartabell i hasura backend på grund av Auth0-reglerna som vi lagt till tidigare. När inloggningen är klar får vi accessToken i JWT-format med funktioner som tillhandahålls av Auth0 SDK i App.js. Denna accessToken kommer sedan att användas som en behörighetshuvud i apollo-klientfrågor för att backend, varför varje fråga som går till backend kommer att ha behörighetshuvud.

Först ändra innehållet i index.js till följande:

Här använder vi Auth0Provider som är en kontextleverantör för Auth0-klienten. Alla barnkomponenter har nu tillgång till Auth0-klienten.

Efter att ha tillhandahållit Auth0-klienten till vår app ersätter vi nu innehållet i komponenter / Header.js-filen till följande:

Vi använder hakenAuth0 (rad 7) för att använda olika funktioner som tillhandahålls av Auth0. isAuthenticated används för att kontrollera om användaren är inloggad eller inte. loginWithRedirect används för att logga in och omdirigera efter inloggning till specificerad omdirigering-url. användarobjektet har information om den aktuella inloggade användaren.

Om användaren är inloggad här tar vi användaren till användarprofilen, som vi kommer att implementera senare. Om användaren är utloggad, visar vi bara inloggningsknappen.

Nu kommer vi att göra ändringar i App.js för att inkludera Auth0-funktionalitet. Ändra innehållet i App.js till följande:

Vi använder useState-kroken (rad 22) för att ställa in det initiala accessToken-värdet till tom sträng. Om användaren är inloggad hämtas tokenet från Auth0 SDK-klienten med getTokenSilently () (rad 33). Observera att den här funktionen returnerar ett löfte och är asynkron. Denna funktion försöker returnera det aktuella åtkomsttoken. Om tokenet är ogiltigt, uppdateras tokenet tyst innan det returneras från funktionen. Om försöksblocket framgångsrikt körs ställs accessToken-värdet in på JWT-åtkomsttoken från Auth0 (rad 34).

Komponenten återges när vi får accessToken-värde. Således efter att async-funktionen har slutförts lagrar vi värdet på accessToken i tillstånd. Komponenten återges och apollo-klienten får tokenvärdet, vilket återger hela ApolloProvider (context-provider) med nytt tokenvärde och autentiseringsrubriken.

När vi har accessToken, kommer vi att använda detta för att göra förfrågningar till vår backend med apolloklient. Se apollo-dokument för apollo-autentisering med hjälp av rubriker. I grunden överför vi accessToken som behörighetshuvud (rad 52) i våra apollofrågor. Denna klient används sedan inuti ApolloProvider (kontextleverantör) för att ge barnelementen åtkomst till apolloklienten som skapats här.

Nu bör du kunna logga in i vår app. Rensa cache och inloggning. Du måste bli ombedd att ge åtkomst till din autoriserade hyresgäst av vår hasura-backend. Ge åtkomst så är du bra att gå.

Obs: Om du står inför fel, kom ihåg att inte hålla något beroende av reakt-apollo. @ apollo / react-hooks måste användas istället.
Nu ser vår app så ut

Implementering av flöde och gillar (realtidsuppdateringar av gillar)

Vi kommer att implementera en lista med inlägg (feed) och en liknande knapp. Skapa en ny komponentkomponent / Feed.js som:

POSTS_LIST-fråga (rad 8) används för att hämta information från posttabellen i vår databas. Vi frågar efter inläggets id. useQuery (rad 18) är en anpassad apolloklient-reaktionshake. Vi får frågedata i dataobjekt (rad 18) som sedan skickas som en rekvisita till postkomponenten, som vi kommer att implementera nu.

Skapa en ny komponentkomponenter / Post.js som:

Här får vi rekvisita som passeras av Feed.js-komponenten och använder id-propellen, vi får fullständig postinformation med POST_INFO-frågan. Vi återger sedan uppgifterna med styling i returmeddelande. Vi använder funktionen timeDifferenceForDate (rad 68) för att konvertera post.created_at till instagram stiltid. Nu måste vi implementera den här funktionen. Vi importerar också Like-komponent som tar hand om samma funktionalitet, som vi kommer att implementera senare.

Skapa en ny katalog src / utils och skapa en ny fil TimeDifference.js som:

Det är bara en verktygsfunktion för att konvertera datumdata till vårt önskade format.

Nu kommer vi att implementera liknande-komponenten. Skapa en ny filkomponent / Like.js som:

Liksom komponenter får post_id genom rekvisita. Här skriver vi två mutationer och en fråga. FETCH_LIKES används för att hämta antalet likes från Post-tabellen. Vi hämtar också om den för närvarande inloggade användaren redan gillade inlägget (rad 15). LIKE_POST och DELETE_LIKE används för att infoga en liknande i Gilla-tabellen och ta bort från Gilla-tabellen.

Vi lagrar countLikes (antal likes) och gillade (om användaren gillar inlägget) i tillståndsvariabler. När tillståndet ändras återges liknande-komponenten vilket ger oss en uppdaterad vy om användaren gillar inlägget. Om användaren gillar inlägget visar vi ett rött hjärta, annars ett vitt hjärta i UI. För att implementera detta kontrollerar vi värdet på gillade (linje 104) och återger knapparna i enlighet därmed. När användaren gillar inlägget ändras tillstånd (rad 109), komponenten återges och liknande mutation inträffar (rad 108) som registrerar liknande i databasen, och antalet likes ökar (rad 110).

Vi har två mutationer, skickar in liknande (rad 58) och tar bort liknande (rad 69). Båda mutationerna använder refetchQueries-argumentet (rad 60) som används för att hämta frågan FETCH_LIKES, och uppdaterar därmed apollo-cachen med nya värden. Detta implementerar giltningar i realtid.

Vi har nu alla komponenter för att implementera postflöde. Vi måste ändra App.js för att inkludera Feed.js. Gör följande ändringar i App.js:

Switch är en del av react-router som används för att matcha komponenter med deras banor. Sätt in några slumpmässiga data (inlägg) från Hasura Console i posttabellen och prova appen.

Nu ser vår app så ut

Försök att gilla inlägg och se realtidsuppdateringarna i likes, tack vare refetchQueries. Vi har ännu inte implementerat användarprofilen, så användarprofillänkarna fungerar inte. Nästa kommer vi att genomföra samma sak.

Implementering av användarprofil

Vår användarprofil har instagram-format UI med användarinformation på toppen och rutnätet för inlägg som laddats upp av användaren längst ner. Vi kommer att implementera profil i två komponenter, den ena kommer att ta hand om huvudgränssnittet och den andra kommer att hantera följefunktioner.

Skapa en ny komponentkomponent / Profil.js som:

Vi har tre olika frågor, som kommer att hämta alla basdata för användaren som ska visas. Lägg märke till att vi kunde ha ringt alla frågor på en gång, men medan vi har tagit fram frågorna vid följdmutering måste vi hämta alla data för att uppdatera cache, men bara följdata skulle ha ändrats. Således har vi gjort två separata frågor för NUMBER_OF_FOLLOWERS (rad 41) och NUMBER_OF_FOLLOWING (rad 31). Vi har exporterat dessa frågor, så när vi implementerar Follow-komponenten, kommer vi att kunna importera och hämta frågorna igen. Detta kommer att bli tydligare när vi börjar implementera följfunktionalitet.

Vi får user_id som rekvisita som kommer att användas för att fråga vår backend-databas för användarinformation, för den givna user_id. Uppgifterna återges sedan i retur (). Rekvisita (user_id) här skickas i form av url, och vi använder props.match.params.id för att få den rekvisiten. Dessa rekvisita tillhandahålls av reaktor-routern BrowserRouter-kontextleverantör, som ingår i vår index.js-fil.

Fråga USER_INFO används för att hämta data från tabellanvändare och post. I rad 103 kontrollerar vi om den visade profilen för närvarande är densamma som den användare som för närvarande är inloggad. I så fall kommer vi att visa en Logout-knapp. Om profilen tillhör andra användare visar vi istället en Följ-knapp. isLoggedUser-funktionen används för att kontrollera detta tillstånd. Följ-knappen implementeras i Följ komponent som vi kommer att implementera nästa.

Vi använder också reager-bootstrap-rader för att implementera inläggs rutnät längst ner i användarprofilen, med tre objekt per rad (rad 147). Varje inlägg i rutnätet är en klickbar länk som leder till respektive inlägg. Här skickar vi id som rekvisita via url (till = {"/ post /" + post.id}) i rad 148, som nås via props.match.params.id i den mottagande komponenten. Detta är ett sätt att reagera router för att skicka prop. Se detta exempel för mer information.

Nu kommer vi att implementera Follow-komponenten. Skapa en ny filkomponent / Follow.js som:

Detta är identiskt med Gilla-komponenten. Det hämtas om den för närvarande inloggade användaren följer den för närvarande återgivna profilen med FETCH_FOLLWERS-frågan. Om data som returneras av FETCH_FOLLWERS inte är tomma, ställer vi initialt följt tillstånd till true (rad 115). Här använder vi ett tillstånd följt (rad 49) för att kontrollera om den aktuella användaren följer den visade profilen och en ref-variabel firstRun (rad 52) som kontrollerar om komponenten återges för första gången, vilket är användbart som vi vill utföra vissa operationer (rad 112) vid första gången rendering av komponenten, som att ställa in tillståndet följt till sant eller fel initialt beroende på data som returneras från frågan FETCH_FOLLWERS.

Vi använder också två mutationer FOLLOW_USER och UNFOLLOW_USER som infogar och tar bort data från Följ tabellen i vår backend. Observera att båda dessa mutationer hämtar tre frågor (rad 66) för att uppdatera apollo-cache med korrekt data efter mutationen. Detta implementerar automatiskt datauppdateringar i realtid, där så snart mutationen utförs, antalet följare på den visade profilen och antalet följande för de inloggade användaruppdateringarna.

Nu kommer vi att göra de ändringar som krävs i App.js. Men skapar först en ny fil som komponenter / SecuredRoute.js som:

Detta hjälper oss att skapa några säkra rutter som endast kan nås om användaren är inloggad. Vi kommer att använda säkrade rutter vid dirigering. Om någon använder försäkrad rutt, om någon försöker komma åt webbadresserna utan att logga in, kommer användaren att omdirigeras att logga in automatiskt.

Gör nu följande ändringar i App.js:

Nu bör du kunna besöka användarprofiler. Sätt in lite provdata från Hasura-konsolen och se användarprofilerna och följ funktionen. Se uppdateringen i realtid i följfunktionen.

Användarprofilen ser ut så här

Implementera funktionen Skicka inlägg

Skapa en ny filkomponent / Upload.js som:

SUBMIT_POST-mutation används för att ange en post i vår databastabell Post. Vi använder Rea-bootstrap modal för att visa en popup-ruta för att ange värden på url och bildtexter. För närvarande stöds inte uppladdning av bilder, eftersom vi inte implementerar någon lagringstjänst för att lagra bilder.

Vi har ett formulär (rad 48) som har två inmatningsfält för bildtexter och url. Vi använder reaktillstånd för att lagra värden på bildtexter, url och fel (om mutationen inte lyckas). Om formuläret skickas, kallas submpost-mutation som ändrar data och uppdaterar uppdaterade uppgifter i apollo-cache för frågor POST_LIST och USER_INFO och därmed uppdaterar flödet respektive användarprofilen.

Nu kommer vi att göra de nödvändiga ändringarna i App.js:

Om användaren är autentiserad visar vi en uppladdningsknapp som öppnar följande popup när klickas på:

Slutligen har vi vår app klar med funktionalitet för uppladdningspost. Du kan navigera till användarprofiler, skapa nya inlägg och se realtidsuppdateringar av nya inlägg, gilla och följa.

Du bör nu ha en fungerande Instagram-klon. Om du vill hänvisa till den, är den slutliga koden för den här appen här. Se live-demo av appen här.

Erkännanden:

TimeDifference-funktion: https://github.com/howtographql/react-apollo

Några stilar tagna från: https://pusher.com/tutorials/instagram-clone-part-1

Om författaren

Abhijeet Singh är UG-student i datavetenskap och teknik från IIIT Kalyani. Han har arbetat inom Full Stack Development, Android, Deep Learning, Machine Learning och NLP. Han deltar aktivt i konkurrerande programmeringstävlingar och har intresse av att lösa algoritmiska problem. Han är en startentusiast och spelar bordtennis och gitarr på fritiden.

Ursprungligen publicerad på https://blog.hasura.io den 6 september 2019.