How to build an eCommerce Website using React Redux, GraphQL, Firebase #15 – Product Details

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome to the 15th video in this video series on building an ecommerce website using react redux graphql and firebase now in today's video we're going to be working on our product details page as you can see we will have our main product image name price and add to cop button and of course the product description however i will be showing you um that in this tutorial we will be adding a new wysiwyg editor into our add new product form so that users can actually add rich text to the product description of course you can add different styles you could add different headings so this is an example heading ordered and unordered lists they can even go into the source and write custom html we've also updated our search page to actually allow users to click through any one of these products either clicking on an image or the product name to actually click through to the product details page before we begin this tutorial please check out my official youtube channel that's youtube.com forward slash simple tut not only to find my other videos but of course also to browse the official playlist for this series again i'm posting a direct link to this playlist in the description of this video there is an official github repository for this project and at the end of each of these videos i'm uploading my code to this repository so if at any point you want to compare your code with mine or you just want to come and clone the entire project then feel free to do that please like comment and subscribe the first thing that we need to do is set up our new product details route so to do that what i'm going to do is head back over to my text editor and within the source folder and pages i'm going to create a new folder for my product details page component so i'm going to call this product details and that's just going to contain a simple index.js file it's going to be a functional component so we need to just import react from react and then we'll create our function so we'll say const product details and that's just going to equal as i said a function and we're just going to return a div for now but what we're going to do here is just export default product details and that will be enough for us to actually create our route so i'm going to come into [Music] my app.js which is where i am currently importing all of my page components and creating my route so i'll say import product details from pages slash product details and then what i'll be able to do is come down and where i've created my search routes [Music] my search routes which are here underneath those i'm going to duplicate one of the search routes to save myself a bit of time i'm going to update the path and the path is going to be slash product slash a a new url param which is going to be product id and of course i'm going to update the page component to render the product details page component we just created so i want to talk to you a little bit about the path that i've defined for this route but specifically i want to talk to you about the product id because this is really important now as you can see what we're currently doing on our search page is we're fetching all the products within our products collection in our firestore now what we want to do on our product details page is very different we are only interested in one specific product but of course we need a way of querying that we need to know which product a user is trying to is trying to visit so of course each one of these products have a unique id assigned to their document and we're going to fetch that document via that unique id and we can handle that within our application by actually passing that id as a url param to a product details page that will give us access to the product id whenever a user visits this page and that is how we're going to query it before we can create a component to render our product data we need a way of actually going out and fetching it from our firestore so to do that we need to write some redux code i'm going to come into my text editor into the redux folder into products i'm going to open up my products types file and we need to create two new types now of course we're using redux saga so we need a start action and we also need another action and and the equivalent type that we can use to actually set the data in our redux store once we've gone out and fetched it so we need two types here the first is going to be fetch product start and the value is going to be the same name as the key and we also need set product and again this is going to be used to actually um set the the state within our redux store once we've actually fetched our product data so of course we've created the types so now we need to create the equivalent actions so what i'm going to do here is create my start action so i'll say export const fetch product start it's just going to take a product id and then it's simply going to return an action with a type and payload the type is going to be from product types and the fetch products fetch product start and the payload is just going to be the product id that we've passed into the action and then of course we need the equivalent action that we can use to set the product so we'll say export const set product this is going to take a product and again it's simply going to return an action with a type-in payload the type is going to be product types dot set set product and again the payload is going to be the product itself now of course once our fetch product start action gets dispatched this is the action that we want to intercept with our product saga so what i'm going to do is i'm going to create my new sagas so we'll say export and we need to create a new generator function so we'll say function with an asterisk that will create our generator function here and then we'll say on fetch product start and then what we'll do is we'll say yield take latest and we want to reference our product types dot fetch product start and then we need to pass it the name of the generator function that will be used to actually handle our our actual action and our asynchronous code so i'm going to create another generator function here so we'll say export function to create a generator function we use the asterisks and then we'll just say we'll call this fetch product and of course this is going to receive the entire action and payload so i'm going to destructure the payload now of course i'm now within fetch product within this generator function this is where we're going to handle all of our asynchronous code however of course before i write that what i need to do is i need to pass fetch product here to the take latest function so that redux can start listening for this action whenever it gets dispatched we need to make sure we're exporting that at the bottom of the file here so i'll say call and pass on fetch product start okay so at this point we're now ready to actually try and handle that asynchronous code and it's going to be really easy so i'm just going to have myself a try catch so we'll say try and we'll catch any errors so we'll catch any error here and of course you could console log the error here but for now i'm just going to comment that out and within this try this is where we're going to actually handle um this is where we're going to actually handle uh going out and fetching that from our firestore so the way that i'm going to do that is i'm going to create a helper so again i have my products helpers file we've already used that previously in some earlier tutorials so what we're going to do is we're going to call this we're going to say export const handle fetch product it's going to take the product id that we're going to get from the payload of the action and then it's simply going to return a new promise the promise is going to take resolve and reject and then what we'll do is we're going to call our firestore which again we're already importing at the top of the file from our firebase utility file that we created earlier in this series and then we're going to call firestore we're going to target the collection which is products we already know that it's our products collection and then we want to get the document within the collection which is our product id that's going to return a reference document that we can call get and that will then give us a snapshot we are going to then take that snapshot and check if it exists so we'll say if snapshot dot exists and if this is true then what we're going to do is we're going to call resolve on our promise and we're going to pass our snapshot but we're going to call data and we're going to call that function which is going to actually return the data that we need and of course if there is an error then what we'll do so here we'll say catch and if we get an error here what we'll do is we'll simply call we'll simply call reject and we will pass the error so this is our helper function and this is how we're going to fetch the the document that we need from our firestore so now if i come back if i come back into my products sagas file at the top of this file where i'm importing from my helpers for where i'm importing my helpers i'm now going to import my new helper we need to make sure we're importing that at the top i'm also going to import a new action from my products actions which is going to be set product because we're going to need both and then all i need to do is come back down to this generator function here called fetch product that we just created and first of all we need to fetch the product using that helper function we just created so i'm going to call product well i'm going to assign this the the response we get back i'm going to assign it to a constant called product of course we need to yield the response but we'll be able to call our helper function which is handle handle fetch product and we're going to pass the payload to this helper function which we know expects the product id but we know from our action we know is is the product id that we want so we're going to pass the product id to our helper function that's going to return the product data and then once we have it we'll be able to yield call yield and put which is how we can dispatch a new action within redux saga and we're going to dispatch our set product action and we're going to pass in the product now of course within our helper we also have a catch here and whenever we call reject within our promise here and we throw that and pass the error here this is why we have a try catch so it would return hit the error here and we could potentially log that out but for now i have that commented out so what will happen now is when we call set products it's going to dispatch our new action which we created here that passes the product however we need to now update our products reducer so that we can update our redux store okay so although we're calling set products and this is passing the product successfully we're not actually currently updating our redux store because our products reducer hasn't been updated to actually listen for that event so what we'll do is we're going to add some new initial state for our product and it's just going to be an empty object by default but we're going to add a new case to our reducer so we'll add product types dot set product and when this matches we're going to again return the state but we're going to update the product with the action dot payload which as you can see from our saga here is going to be our product okay so at this point we've successfully fetched the product from our firestore but we now need a component that can actually render out our product details so we need to create that now you already know from earlier in this tutorial we created our product details page component but now we need a component as i said to render the details from our redux store so we're going to create a new component we're going to call this our product card so i'm going to call this product card and again of course this is going to be a functional component but we are going to have a style sheet so i'm going to have styles.scss and within my product card component we're just going to import react from react it's a functional component so it's just going to be product card it's going to equal a function and for now let's just return a div we'll need to export this so we'll say export default product card and of course we want to make sure we're importing that style sheet so i'll just do that at the top styles.scss cool and just to see that this is working i'm just going to put in here test let's come over to our product details page component and render that out so i'll say import my product card from the components folder so we'll go into component slash product card and we'll just render the component now let's come over to our browser and let's manually enter that into the url so let's say product slash a random number which in this case will be an id of course we're not actually doing anything with this just yet but that's enough to see that the route is working and we're able to see that dummy text that i put in there successfully now before we actually move on uh what i want to do is talk to you about how the well the changes that we need to make to our search page we need to actually add links into this page that will take the user to the details page and again that's going to be really easy for us to do but let's look at how we are actually going to do that so what we're going to do is we're going to come back over to our text editor i'm just going to close these quickly and we have our product results component we created this in one of the earlier videos in this series and i'm going to go into my index file here and as you can see here what we're doing is we're getting the products and then we're mapping through and we have a product component here now at the moment we are destructuring the values that we want to render so that's the thumbnail the name and the price and we're passing that to the product component now what i want to do is i want to pass the entire product into the component because there's going to be another field that we want to use on that so it's just going to be a little bit easier to pass in the entire object into the product component now within the product component what we're going to be able to do now is the structure at the top here the document id so we're going to get the document id and this is the id that we're going to use in the url so this is very important so we also want to make sure that we only render this when we have the document id otherwise we will return null and then what we'll do is we need to make sure at the top we're importing something from react router dom which is the link component and then what we'll be able to do is wrap the image with a link we're going to set the to path to equal slash product slash the document id and we're going to do the exact same thing for anything else that we want to link so i also want to link the product name so again i'll say link we're going to specify the to path it's going to be slash product slash the the document id and again we just need to wrap the product name with our link component and now let's come back over to our browser let's go back to our search page and you can see that the links have changed color they're blue and that's because these are now links and we haven't styled them appropriately so what we'll do is we'll come back again to the text editor we're going to go into our style sheet we're going to find our our name here and we're just going to make sure that we're styling any any links that we might have so we'll just target the anchor tag and we'll set the color to be black let's come back to the browser you'll see that's changed but now both the image and the name of this product is now clickable and if i click it you're going to see that i am taken to the appropriate address so you can see it slash product slash the product id i can come back here to click on another product and you're going to see that you get a different id in the url path and this is the id that we're going to be using to fetch the product data from our firestore okay so we're now ready to build our product card this is where we're going to dispatch our actions to actually go out and fetch our product data and we can do that now using the id that we are storing in the url params so what i'm going to do is at the top here i'm going to import a few more things i need to import the use effect react hook and i need to import some other hooks from two of the libraries the first is going to be the react router dom library and i'm going to import use params this is what we're going to use to actually access our the product id we're storing in our url params so i'm going to say const and we're going to destructure from url params we're going to call that as a function that will give us access to our product id we're going to be able to use that a little bit later but of course we also need a way of dispatching our actions so we're going to import from from react redux and we're going to import our use dispatch and use selector hooks but of course we don't have anything to dispatch yet so we need to import our actions so we're going to import that from our redux folder product and our product actions we want to import two things first is our fetch product start but we also want to import set product okay and the reason for that is going to become clear a little bit later okay so what we need to do now is actually dispatch our action to fetch the data so to get access to dispatch we're going to create a const called dispatch it's going to equal use dispatch we're going to call that again as a function and then what we need to do is actually use our use effect hook to dispatch it so we'll say use effect that takes a function it has a dependency array and there is actually not going to be any dependencies so this is only going to run when the component is initially mounted but we want to call dispatch and what we want to dispatch is our fetch product start and as you can see from this action this expects our product id luckily we have access to that here so all i have to do is pass that in at this point we have dispatcher action to actually go out and fetch the data but it's now going to be stored within our redux store so to access that i need to create a map state function so i'll say const map state it's going to take the state and then we will be able to get the product from our products from our state products data and the product then to access that all we'll need to do is call so we'll say cons product is going to equal use selector and we're going to pass in our map state function and then from that we'll be able to destructure from our product whatever we might want so in this case i'm going to start off by just destructuring the product name and i'm going to replace my dummy text here which is at the moment test with a h1 with the product name okay so let's come over and test this in the browser i'm going to click on one of these products okay and i'm not seeing anything on the page i should be rendering out the product name so let's look at the product data and you can see that we're not actually getting anything in the redux store which suggests there's something wrong with our saga so i'm going to go to my product sagas maybe my helper ah okay so this was a bit of a gotcha here uh i have my collection here in my firestore i i said it's product it's actually products um so let's look at this again in the browser i'm still not seeing anything on the page but it looks like i'm getting the product data now so my redux store has it but you can see that it's within product so actually if i come back over to my index i need to destructure product from um the response from the redux store so i should now be rendering out as you can see here the the product name so if i was to come back to search and click on a different product let's say product six you'll see that i render the correct product okay so at this point we're pretty much ready to actually build our page and our product details component or product card component so what i'm going to do is i'm going to start off with a div i'm going to give this a class name of my product code within this i want to have my main product image so i'm going to have another div here i'm going to give this a class name of my hero and that means that i need to actually come up here and destructure from product i need to destructure the product thumbnail now once i have that that will give me the source url for the image so i'll create a new image tag and i'm just going to pass the source url which is going to be the product thumbnail and then i want to render some details under that so i'm going to have another div and i'm just going to call this product details so give that the appropriate class name so i'll say product details and within this div i'm going to have an unordered list with list items the first list item is going to contain a h1 which will be the product name i'm already restructuring that up here like that underneath that we're going to have our price so i'll create another list item but we'll need to destructure the product price here so under prep name we'll say product price and again we're going to render that here that's going to be inside of a span we're going to have the currency symbol followed by the product price and underneath that what i want is to actually have a add to cart button so i'm going to import my shared button component so i'm going to import my button from my forms slash button component but what i will need to do is i'll need to create a config object so i'm going to create const config config add to cart btn equals we're going to pass the type which is going to be of type button and then what i'll then be able to do is under my price i'm going to have another list item here and this is going to have a div with a class name of add to caught and within that i'm going to render out my button component but i'm going to pass in my config object so config add to cart button and the text inside of the button is just going to be add to cart okay so that looks pretty good um but what i also want to do is just add some basic styling to this component so again we have our product card wrapping class which i'm going to target so i'm going to target my product card i'm going to be setting a max width of about 800 pixels so again i'm using rams and my root font size is 10 pixels so i can divide any number by 10 that will give me the equivalent rem value i need to assign so 800 divided by 10 is 80.0 rem that's 800 pixels i'm going to set a margin of 0 auto and i think 100 pixels so again that'll be 10.0 ram i'm going to target my hero image which is that main product image i'm going to set this to be display block i'm going to set a width of 100 and i'm going to set the margin of it to be top and bottom 20 pixels which is going to be 2 ram auto to center it i'm also going to target the actual image element and i'm just going to make sure i set this to be display block and a width of 100 and a margin of zero just to make sure that is how i want it to look and then i'm going to target my details so i'll say product details and i'm going to target the unordered list the list items as well i'm going to reset the margin and padding to zero on those and then i'm going to target the list items i'm going to set the list style type to none and i also want to set the margin on these to be 0 auto and well 0 on top i'm going to center it and i'm going to set 10 pixels on the bottom i'm then going to target the h1 i just want to set the margin to 0 on that and then finally what i'm also going to do is i'm going to target my add to cart class that was the div that i wrapped my add to cart button in and i'm going to just set a max width on this of about 200 200 pixels okay so i think that's about it let's come back over to the web browser now okay so the add to cart button is uh is not inherited the 200 pixels that i had previously assigned so i'm just going to check this again in the code okay so i misspelled the class i wrote cod instead of caught so let's return again to the browser and you'll see that the add to cart button is more appropriately sized but otherwise this looks great so okay so that looks a lot better um but there's still something that we need to do that's very important i'm going to come over to i'm going to open up my console and i'm going to come away from the detail page now what i want to show you is the current state that we have in our redux store for our product so you can see that we are currently storing the last visited products data even though we are no longer on a detail page this is going to lead to a ui bug because if you notice here if i come over and we're on product one if i come over to the search and click on a different product you're going to see that there is a jump you you saw that switch to product three from prep one so there's a bit of a jump between the time that it takes for the uh for us to get a response from our firestore with our new product data and update the content on the page for a split second we're actually displaying the previously visited products data so to fix that what i'm going to do is as i mentioned previously i also imported set product this is the action we use to update the product state within our redux store now we are using the use effect hook to dispatch our action and fetch the product that we want to see on the page but the use effect hook also allows us to return a function and this is used in a similar way to component will unmount it's used to unsubscribe from event listeners and various things like that but what we're going to use it for is to dispatch another action which is going to be our set product action and we're going to reset that back to an empty object and what that means if i come back over to the browser and i open up my console again as you can see currently in our redux store we have our product data stored within our state if i now come away and i go to our search page and i check our data again you're going to see that our product state is empty and that is what we want okay so at this point you can see that we've successfully built a product details page we are displaying the product data on the page however you might feel like something is missing right because really we're displaying the same data that we have in our search results on our detail page i would expect to see something more and what i'm thinking about is text a description information about this particular product now at the moment we are not storing that kind of data within our firebase documents as you can see here the the data that we're storing is just really name price thumbnail category and other unrelevant and other relevant information like that so what i want to do is i want to add a new field which is product description and i also want to allow an admin when they're creating products to use a wysiwyg editor to actually create more rich html content uh effortless effortlessly i'm going to come back over to my terminal window and i'm going to install a new dependency so i'm going to install npm install ck editor for dash react okay i'm going to install that dependency which is going to take a few seconds and then i'm going to restart the application because we're going to need this dependency going forward okay so once that's done installing let's do npm start and that's going to restart our application of course we now need to actually use it so what i'm gonna do is i'm going to create come back over to my text editor and what i want to do so the first thing that i want to do is just show you in the browser what we're going to be doing so i need to log into the application so i have a test account so it's admin at admin.com enter my password and i'm going to come back over to my admin and what you're going to see is that if i go into add new product all i currently allow users to do is add the category the name an image and a price i want to add a new field which is going to allow the user to enter a description and i'm going to use the wysiwyg editor ck editor to actually allow the user to do that so what i'm going to do is come back over to the text editor i'm going to close all of these files we don't really need them right now and i'm going to go into my admin page and this is where we are actually building our ad product form and we need to insert our new field so at the top of this file i'm going to import that new dependency so i'm going to import ck editor from the dependency itself which is ck ck editor for react and i'm going to create a new piece of state so i'm going to say const and i'm going to call this my product description and i need to create that update function so i'll say set product description it's going to equal use the use state hook which is going to by default equal an empty string and then i'm going to come down and underneath the price form input component i'm going to render out ck editor right which is going to be very very simple and easy for us to do and it allows us to call the onchange event right it is going to pass the event and that will allow us to call our set product description function and we are then going to target the event dot editor dot get data and we're going to call that as a function and that is going to pass us back the data that we need i'm also just going to add a line break here so that there's a bit of space between the editor and the the button okay so let's actually look at this in the browser okay so i'm logged into my admin account i'm going to come into my admin and i'm going to go add new product and you can see that i have my new field which is a wysiwyg editor it allows me to enter a rich text so i could enter bold text here so this is an example of bold text underneath that i could write some standard text so you know this is some info i could create a numbered list so you know first second third i could create bullet points here is something if i wanted to i could add more styling formatting i could make this full width so i had more space i could go into the source code and you can see that it's writing html for me you know there is a lot that you can do with this wysiwyg editor but of course at the moment although it appears in the ui we're not actually going to do anything with that within our collection within firebase because it's not coded to so what we need to do and as you can see here we have our form which calls handle submit and this is a very simple function it just dispatches an action which passes um the various data we want to store within our our collection so we want to now pass that additional uh description that we've created so we're going to pass product description right and we're going to store that in our collection when the form is also submitted we also want to reset that value in the form so we'll say set product description and we'll call that and reset that back to an empty string but if i just take you to this action which is the add product start action let's just run through this and make sure everything is going to work as we expect so at product start dispatches add new product start and passes the product data that's fine so let's now go into our product saga and look for that so we have uh where is it on product start which calls at product and as you can see here we're manually uh we're destructuring from the payload the values now i could handle this in a number of ways i could manually say product description uh here and that would be fine but rather than that for a little bit more flexibility what i'm going to do is just destructure the entire payload and then i'm going to spread that in to the handle add product helper function and these are the two custom fields where i'm getting the user's id and i'm also creating a new timestamp so that's fine but i'm going to spread in the entire product object so that what that means is that if in the future i add more to this it will automatically be passed to my saga rather than me having to come back here every time and pass that manually and again if i click into this again this just takes the entire product so i don't need to do anything further here that looks good so what i'm now going to do is i'm just going to i'm going to test that so i'm going to create a new product and i have some lauren ipsum text which i'm just going to paste in here let's maybe make this bold and add some custom styling here just to see how this kind of looks okay yeah that looks pretty good and finally i'm just going to click on add product okay so as you can see this is the product with the product description if i actually show you in the console and i show you the our current state our redux store you'll see that we are already fetching the product description um from firebase so we're actually ready to render this on the page so what we're going to do is come over to our text editor and we're going to return over to our product card component so let's go back into components product cod and go into our index.js file and we want to destructure from our product our product description now the first thing i'm going to do is i'm just going to create a new list item underneath our add to cop button and i'm just going to render the product description and i'm going to show you what happens and as you can see although it renders we are rendering the actual html tags which isn't really what we want we want those tags to be uh used as actual html because that's that's what it is the wysiwyg editor that we're using which is ck editor will actually create rich text for us it will create you know all html to style the text as the user creates it so to do that what we need to do is come back over to the text editor and rather than just render the description we're going to create a span and what we're going to do is we're going to set dangerously set inner html and we're going to set the html value to be that of the product description right so what we can now do is come back over to the browser and see the difference and you'll see that the html is now actually rendered on the page right so for example if i inspect this element here you're going to see that this is within a h3 and a strong tag which is bold we have paragraph tags so it's actually inheriting the real styling if you wanted to target this more specifically we could even add a class name here call it description and you could target this within your style sheet but for me this looks absolutely fine okay so that brings us to the end of this video tutorial just to recap what we've done we have updated our search results to allow users to click on products and click through to a details page on that details page we render the product thumbnail name price and add to cart button and the product description and of course if we review the back end we've also added ck editor which is a rich wysiwyg editor to allow users to enter rich text into the product description before i sign off i just want to remind you guys that at the end of each of these videos i'm creating pull requests and i'm merging my code into the official github repository for this project as always i will be posting a direct link in the description of this video to this repository for you to check out i'll also be posting a direct link to the official youtube playlist for this series where you can reference previous videos and future videos but of course as always i just want to encourage you guys to check out my youtube channel please check out my other videos please like comment and subscribe and i look forward to working with you in the next video
Info
Channel: SimpleTut
Views: 3,404
Rating: undefined out of 5
Keywords: ecommerce, react, react redux, GraphQL, React Context API, Node, Node JS, redux, online store, stripe, stripe api, shopping card, paypal, firebase, react router, react router dom, routes, routing, Google Sign-In, Google Auth, Google Sign In Authentication, login, signin, react hooks, useEffect, useSelector, useDispatch, redux hooks, useState, redux saga, saga, user roles, admin backend, ckeditor
Id: ZWfdQ0UNTTM
Channel Id: undefined
Length: 50min 59sec (3059 seconds)
Published: Sat Sep 26 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.