Create a Table in React | Learn how to view, add, delete and edit rows in a table from Scratch

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this video we are going to learn how to create a table based on an array of data we will add new data to your table using a form we will make inline edits and delete rows we will be building this one from scratch and the links to the completed code are in the description i've used create react app to create an empty react project here nothing too special just the index files and an app.js file with an empty component within it if you want to follow along this is all you need to start we will start this one by creating the table and adding some static data in react we create tables using the same tags as we would if it were regular html so in our app.js file we will add a div with a class name of app container this just helps us position the table within this div we will add the tags we need to create our table first we'll add the table tag which indicates that this is the start of our table then we will add the table head tag which will contain our column headings this isn't required but it helps to differentiate different things when looking at the dom so in here we will add a row using the tr tag and within this we will add our table headings using the t h tag so we are going to have four columns to begin with which is the name address the phone number and the email we will add this actions column and later so we'll go ahead and add these to our table so we'll say the name address phone number and email so that's our headings in place now we will do similar with the body we will take a new line after the table head tags and add the table body tags and here is where we put the rows of data that we want to display in our table so again we'll add a qr tag and since we have four columns we need to add four table data tags with some static data so we'll add a td and we will add a name say jenny chan and for an address then a fake address and a phone number and finally we'll put in a email address each of these cells matches up to one of our columns in the header to make the table look a bit nicer i've added some simple styles which you can grab from the github link in the description just click on the link and down here you should see a bunch of styles so what we want to do is copy everything and back in the code we want to create a new style sheet in the source folder called app.css and then just paste all the styles you copied in here and click save remember to import it into app.js go to import open quotes dot slash app dot css now if we start this by opening the terminal and typing npm start you can see our table has appeared so we have our four column headings and we have a row of data just below it this is nice but it's not very useful since the data is static what we want to do is dynamically render the rules based on some sort of data so again i've created some mock data for us to use if you're following along go to the link in the description and copy the json which you can see here so it's just an array with some data so we'll copy this and back in our code in the source folder we will create a new file called mock dash data dot data and we'll paste it in like so this data is an array of contacts each object in the array has data that matches the columns in our table so we have the name the address and so on now in app.js we're going to import this so say import data from open quotes dot slash mock data dot json now we have an array of contacts we are going to pretend that we called an api and this is the data that we got back in other words we are just mocking the data since our table blows our dynamic and the data is likely to change it makes sense to store this data instead that way when the data is updated react will update the ui components for us so just at the top of our app component we will take a new line and then we'll say const contacts comma that contacts it's going to be equal to used it and we'll import the new state hook from react and we want to initialize our new state hook with the data that we got from the mock data.json file so we'll just pass it in like so so all we're doing here is taking the data and storing it in state using the used hook and now this state is available via the contacts variable and we can update the state with the set contacts function now we have some data to work with what we want to do is render a table row for each object in our array to do this we will use the map function to flip over our array of contacts which we just stored in state up here and output the row so just inside the table body tag we will take a new line open some curly braces and then we will say contacts dot map open our braces and we're going to pass in an arrow function and then we're just going to cut the current row that we have so everything inside the tr tags and just paste it after our arrow so now we have a map function which is going to output a row for each contact in our contacts array so now we just need to display the data for our given contacts as opposed to this static data that we've added already so as the map function blips through the contacts array it will give us access to the current contact object it is currently on and passes it to our function as a variable we will name this variable contact so now we have access to the contact object we can display the data in our table row so we will delete this hard coded stuff and for the first column we will say contact dot bill name contact dot address contact dot phone number and contact dot email so all these properties are just coming from the contact object which is part of the mock data array so a full name address phone number and email and we are just displaying these in our table row so now if we try this in chrome you can see we have a bunch of rows each one has a contact which is taking from the data in our mock data file this is cool because if we change the data it all just works so if we add a new row to our mock data json file so i'll just copy one here and paste it and then we'll say new contact just so we can see things change jump back into chrome you can see it's appeared this is how we make table rows dynamic in react it doesn't matter where your data comes from whether it's an api call props or whatever you just store the data instead and map over it in the body of your table now we are going to look at adding data to your table with a basic form so in our finished app we have this form which appears at the bottom if we add some stuff and and click add it appears in a row there are a number of form frameworks you can use to create a form but to keep things simple we're going to use a plain old html form let's start by putting our form in place so just below the closing table tag we will add a h2 tag just to separate things out and then we'll say add a contact and just below this we will add our form tag so in here we will add four inputs one for each of our columns so we'll do an input the type will be text the name we are going to name the same as the property in the object in our contacts array we'll say full name which is what we've called it up here and we'll say required is required and then we'll just add a placeholder it says enter a name so now we're just going to copy this one and paste it just below and change some things to match our columns so the next one we have is address so say the name for this input will be address and we'll change the placeholder under an address and we'll do the same again for the next one which is phone number so we'll say the name will be boom boom number enter a phone number and the last one is email i think yep so we will add an input to capture the email change the name to email and we'll say enter and email and the type for this one is going to be email and finally we're going to add a button with a type of submit and then we'll say add for the text on the button so when this button gets clicked provided the form is valid it will submit the form so if we have a look at chrome you can see our form has appeared it doesn't look the best but we'll do for now and if we try to submit this without the values being correctly filled it will give us a plain old error which is what we want so when working with forms like this in react we need to store the form values and state and update the value as the user types the simplest way to do this is to store the form values as an object in a single state hook and use an event handler function to update the values back in our code at the top of our function just under the contacts state took we want to add a new one so we'll say const add form data so this is the data for our add contact form and we'll say set add bond data and this will be equal to used and we're going to initialize this to be an object so in here we're going to have a different property for each input field in our form we'll have one for fill name address phone number and email so we'll just add these in we'll say full name and we'll default this to an empty string address phone number and email notice how the spelling and the case are the same as the name attribute which we give to our input in our form this is because we're going to use the name of the input to determine what input field and input value has changed now we will create a function that gets called when the user changes a value in any of the inputs so all our inputs will be linked to this new event handler that we will create just beneath our state books we will create an arrow function called handle add form change and this will be equal to an arrow function that accepts the event and here we want to do event dot prevent default the first thing we want to do is get the name of the input the user has changed so in here we'll say const big name is equal to event dot target dot get attribute and we'll pass in a string and within the quotes we will say name so this will get the name attribute of whichever input the user has typed into and assign it to the field name variable next we want to get the actual value that the user has entered into the input we'll say const field value is equal to event dot target dot value next we'll make a copy of the existing form data so that we can change it without mutating the state we'll say const new or data is equal to open our curly braces do three dots and then we'll say add or we're just using the spread operator to copy the existing form data and assign the new data to this variable next we will update the object with the new value the user has typed so we can say new form data open our square braces pass in the field name and this is going to equal to the field value remember that the new form data variable is an object so we can use the square brackets and text to get a given key so for example if our field name is phone number this will get the phone number key from our object and assign whatever the user has typed to it lastly we will set this into state by saying set add form data and passing in our new form data now we just need to call this function when any of our inputs change so to do this in our input tags we will add it the on change property and this will be equal to the handle add form change when we specify a function call like this react will automatically pass in the event for us so all we have to do is copy this and paste it into the rest of our inputs so we'll paste it into the address into the phone number and finally into the email field as well so now if we open up chrome and open up our dev tools and if we have a look at the components tab you can see in our app component we have our state hooks and the second hook has our add form data so it starts off as empty strings but as we change some of these you can see things start to update in state as well so the reason why we do it like this is because it makes it easier to add new values to your form we just add a new input to our form add a new property to the initial state and call this event handler and it will all work this also means we don't have to have separate event handlers and state hooks for each input in our form which can make the code quite repetitive and hard to follow have a play around with this and understand how it works it's a good piece of code which can which can be used almost anywhere that you need a form so it's good to know how it works now we have our form values stored in state and some basic validation we will add a function that gets call when the form is submitted so just below our handle add form change function we'll create a new function called handle add form add form submit and this will be equal to an arrow function which takes the event and just like before we want to do event dot prevent default just to prevent the form from doing an a post request when it's submitted and here what we want to do is take the data that the user has entered into the form which we have stored in the add form state object and create a new object from this so we'll say const new contact it's equal to an object so say the fill name it's going to be equal to add form data dot full name address it's going to be add form data dot address phone number will be add form dated will be add form data phone number and finally email will be add form data dot email like so and we also need to add an id in here because we will use the id later to identify which contact we are editing or trying to delete so we'll say id and we're going to say nano id open and close braces so we just need to import this so we'll scroll to the top and then we'll say import open braces nano id from nano id all this does is generates an id for us and back in our function we will create a new contacts array to avoid mutating the state we'll say const new contacts is equal to open braces and we will copy the current contacts by using the spread operator and then we'll do a comma and then we will add the new contact object that we just created to the end of this new array like so and finally we'll do get contacts and pass in our new array remember any contacts we add must have the same properties as our existing contacts or else things will act a bit funny so now we can call this function when our form is submitted so we'll scroll down to your form and then we'll add the on submit properly and this is going to be equal to our function so we'll save this and now if we try in chrome if we refresh this we'll say it is a new contact and then just add some other stuff in here and if we click add you can see it appears at the bottom now if we refresh this will disappear because it's only saved in a local state this next feature is a pretty good one i get asked about a lot how do you edit a table row in line what we're going to do is effectively have two rows one row is going to be hidden and will contain the inline form which looks like this the other row will be a visible read-only row like we currently have we will have an edit button which when clicked will display the row with the inline form whenever we click the save button this will update the state okay so we're in app.js most of the work for this section will be done just within the table body tags where we display the rows things could get a bit messy in here what we're going to do is split things out into different components so that they're easier to follow so we're going to have a component for the read-only row which is what we currently have and we will create a new component for the inline form row so in the source folder we're going to add a new folder called components and in here we will create a new file called read only row.js and in here we will create a react component as normal and we're going to jump back into app.js and we're going to cut and paste the current row markup from inside the map function and paste it inside the return statement of the read-only row component like so as you can see the ide is saying there are some things missing that the component relies on we will pass these in at as props whenever we pass props to a component we can destructure it so we'll add our curly braces and and here we will say contact when we render this component we just see in a minute we're going to pass in the contact and our component can render the details based on the contact object so now we'll jump back into app.js and render our new component in place of the old code by saying read only row and we will pass in the contact as a prop so we'll say contact is equal to contact and we need to import our component so we'll scroll to the top we'll say import read only row and it's auto completed for us which is good so now if we save this and have a look at chrome it should work the same and it does which is good next we're going to create our edit row component this is the component that holds the inputs that lets our user update the values for a given row so again in the source components folder we will create a new file called editable row.js and we'll create our component this component will be similar to the read-only row component in that it will have a table row so we'll add our tr tags with a bunch of cells add a few tds in here we will add four because we have four columns each cell in this row will have an input and we will keep these inputs in the same order as our columns so in the first pd we're going to have an input and we're going to say type is equal to text we'll say required is equal to required placeholder will be enter a name and the name of this input is going to be full name so this is the input that lets the user edit the full name for a given contact in a row we will do the same for the rest just like before we're going to copy and paste the input and change the things that we need so the next one is address which is our second column so in here we'll say enter an address and the name will be address and we will do the same for the phone number and the same for the email so we'll say please enter a phone number and the name is going to be phone number and we'll do one more for email please enter an email and the name will be email and we'll change the type to be email as well okay so that's our inline form in place for now we will render both rows so we can see what's happening in app.js render the editable row just above the read-only row so in here we'll say editable row and we need to import it so we'll say import editable row from dot slash components slash editable row and add a semicolon at the end and now we'll see if so now it's saying we can't have two children components like this so we need to add a react fragment so we will import the fragment from react and just inside the map function we'll add a fragment tag like this and then we're going to cut our two components take a few spaces inside the fragment tag and paste it in and if we save this looks good now we have our two rows what we ideally want to do is wrap the editable row in a form tag so that we can handle the form submit and capture the inputs which live in the editable rule component although when we do this we get a problem in chrome which says invalid dom nesting a form tag cannot appear as a child of the table body which is a problem what we need to do is instead of wrapping our components in the form tag we will remove these and we will wrap the entire table in the form tags instead this approach is fine as we will only ever have one set of form inputs being displayed on in the dom at any one time which means the form inputs will not be duplicated and cause any funny issues okay so now if we save this and have a look at chrome you can see there are two rows rendered for each contact in our list one is the read-only view and the other is the view to edit the fields okay so now we have this in place next we'll look at toggling between these two rows and adding a button which will let us save the new values that the user entered as you can see in the finished app here whenever we edit we have the save button in the actions column since we only want to edit one row at a time what we will do is add an edit button and when this button is clicked it will store the id of the contact we are editing instead we will then use this id to determine whether to show the editable row or the read-only row when we render the lists of contacts so in our code we will start by adding a new state took we'll say const edit contact id comma set edit contact id is going to be is going to be equal to use state and we will default this to no so if the edit contact id is null that means the user isn't editing any row now we want to add a ternary to render either the editable row or the read-only row depending if we have an edit row id or not so in our fragment we'll take a new line open our braces and then we'll say edit contact id is equal to the contact dot id and then we'll do a question mark and then we will paste in our editable row just after the question mark and just outside the closing brace of our component we'll add a semicolon and just after the semicolon we will paste in the read only row component and if we save this it should format it nicely for us so as the map function flips through the array if the id of the current contact object matches the id stored in state in the edit contact id statehook then it will render the editable row if not it will render the read-only row this syntax acts like an if statement and is a good shorthand way to determine between one or two options we can test this by looking at our mock data.json file so our second element yes ago born has an id of two so if we store this in our initial state in app.js so we'll set the edit contact id initial state as being 2 it should render the editable row component for jessica if we refresh you can see it has before we move on to pre-filling and saving the form for an edited row we want to add an edit button to each row which will update our edit contact id static so in app.js we will add a new column to our table header called actions for now we will have an edit button but eventually we will have some other stuff depending on this date before we add the button we will add the event listener function this will be called when the user actually clicks the edit button just beneath the handle add form submit function you will take a new line and say const handle edit click and this will be equal to an arrow function which will accept the event as well as the contact so this accepts the contact because we need to know the id of the contact for that row so that we can save it into state so in here we will say event dot prevent people and then we'll do set edit contact id and then we'll pass in the contact dot id so we put this event handler function here so that it's close to the state that needs updating in this case the added contact id state object next we will pass this function to our read-only brow component as a prop as this is where our edit button is going to be so we'll say handle edit click will be equal to handle edit click next we will jump into read only row.js and we will restructure the handle edit click prop by saying comma handle edit click and we will add a new row cell at the end by adding another td in here we will add a button which calls the handle edit click function so it'll say button but type equals button and we'll give it a text button and then we'll say on click equals an arrow function and the event will get passed here function by react and then we'll say handle edit click passing in the event and passing in the current contact which we get from props remember we need the contact so that we can get the contact id and save it into it so let's try this in chrome and actually we will jump back into app.js and remove the value we added to our edit contact id back to null now we have the edit buttons up here if we click edit for this it changes the row to be the editable row and if we click edit for a different row it does the same whoops this last one looks a bit funny so if we refresh let's have a look at our mock data to see what happens and i think it's because we added one earlier and they have the same id yeah so because these both have the same id when our map function runs our edit contact id which is saved in state is going to be five so it's going to render the editable row for both of these rows so what we'll do is delete it for now and save and we'll try again so if we click edit for a row it toggles the rows and if we try it everyone it resets the old row and toggles the zero so this works because whenever the button is clicked the state changes and sets the edit contact id the component re-renders because the state changes and our ternary operator runs again now we have the editable row showing up we want to repopulate the form and we want to add a save or cancel button to the actions column for that row remember the editable row is essentially a form similar to our add new contact form so we will start by creating a new state object to hold the form data for when we are editing a given row so in app.js just beneath the add form data statehook we will take a new line and i'll say const edit form comma set edit form data let's be equal to use state and just like before we're going to pass in an object which has the same properties as our form inputs what we're going to do is actually copy this and paste it in you could possibly do something a bit clever in here and reuse the same statehook depending on adding or editing a form but for the purposes of clarity we're going to use two separate statics next similar to what we did with our add contact form we want to update the state when any of the form values change we will create a function similar to the handle add form change so we'll take a new line just beneath this function say const handle edit form change which will be equal to an arrow function this function will accept the event in the body of the function we will say event dot event people just like before and we will get the field name this would be equal to event dot target dot get attribute and we'll pass in the name and we'll also get the field value so i'll say conf field value equals event dot target dot value now we want to create a new object based on the new values so that we don't mutate the state we'll say cons new form data is equal to open braces to create a new object and then we will use the spread operator to copy the edit form data just like we did previously we're going to use these square brackets and text on our object to update the value for a given field so we'll say new form data open and close our square basis we'll pass in our billing name and it's going to be equal to whatever value user has typed which in this case is field value and then we will set this to state so we'll do set edit form data and pass in our new form data now we have a way to store the form data we want to pre-populate it with the contact data from that row when the user clicks the edit button in our handle edit click function we already pass in the event and the contact as parameters so all we need to do is take the values from the contact object and save it to our edit form data so we will create a new object called form values this will have the same properties as our edit form data state object up here so it's going to have a full name address a phone number and an email the same as the values we have been working with so far so in our form values object we will say fill name this will be contact dot full name so remember the contact gets passed in when the edit button is clicked it'll say address sql to contact dot address the phone number will be contact dot phone number and the email will be contact dot email and we will set this to state by saying set edit form data and pass in the form values so now we have a way to save the values to state and an event handler to update the state we can pass these to our editable row component so in the editable row component we will pass our form data so we'll say edit form data is going to be equal to edit form data and we will pass in our function to update the form values in state when the user types by saying handle edit form change is equal to handle edit form change in our editable row component we will destructure these so we'll say edit form data and handle edit form change so we want to call our handle edit form change function to update the form values in state whenever our inputs change so we will add an unchange event to each input we'll click on change it's equal to handle edit form change and we will paste this the rest of our inputs so anytime each of these inputs change we will store the value for that input instead similar to what we did with the add contact form so we want to pre-populate the input field values with whatever is in the edit form data prop object so we can add a value to each input we do this by specifying the value prop on the input so let's say the value is going to be equal to open braces edit bar theta dot bill name and we will do the same for the rest so we'll say value is equal to edit form data dot address for the phone number is equal to edit form data phone number and lastly for the email we'll say value equals edit form data dot email lastly we will add a new table cell to this row with the save button in it even though the actual form tag is outside the component it doesn't matter this button will still submit the form because it's nested within the form tags when it gets rendered to the dom so we'll add a td and if we go ahead and add a button it will be submit so that it so that it submits the form and the text will say cf now if we try this we should have an editable row with pre-filled values for a given row so we'll click edit on tony frank and as you can see it's displaying the editable row component which has our form and let's also pre-fill the values for that row and if we click another one you can see our component re-renders and it does the logic to work out which row to show and repopulates as well this seems to be working fine so let's check if our state is updating so unfortunately these hooks aren't named but they are in order so if we look at our code in app.js you can see the edit form data is the third hook that's the one we look for in the dev tools so our third hook is here if we expand it you can see each time we add it or click the edit button that our state is getting updated with the data from that row and let's see what happens if we change the address so if we delete it you can see this it's getting updated if we type some stuff it's getting updated as well so even though our save button is not working we are able to check that our logic to see if the actual form values instead is working this seems to be looking good now we just need to see if the form when the user clicks the save button similar to the add new contact functionality we will create a new event handler function in app.js so just beneath handle add form submit we will say advanced handle edit form submit this will be equal to an arrow function that accepts an event and here we will do event dot prevent default so the page doesn't try and do a post on form submit and just like before we will create a new object based on the new data stored in the edit form so we will say const edited contact will be equal to an object and we will say full name is edit form data dot build name and scene for address address will be edit form data dot address phone number will be edit form data dot phone number and email will be edit form data dot email we also want to keep the id in place as we needed to determine what rule we are editing so we will take this from the edit contact id so just below this we will create a new contact array so we don't re-hit the state must be equal to an array and we will copy the existing contacts by using the spread operator and the contacts variable this time instead of adding the new contact to the end we want to replace the contact object in the contacts array with this new object we just created but we are editing the second rule we want to update the array of position one and if we're updating the first one we want to update position zero and so on so the first thing we need to do is get the index of the road that we are editing back in our function we will say const index is equal to contacts dot find index open braces and we'll pass in a function so defined index array method will return an index based on a condition that we pass in so to find index function we'll pass the current contact to our arrow function and then we want to say find the index of the contact.id is equal to the edit contact id remember the edit contact id is the row that we're editing we want to get the index of that row in the contacts array now we have the index we can update the array at the given position it'll say new contacts open our square braces and we'll pass in the index to the contact at this position in the new contacts array is going to be equal to our new contact that we created up here called edited contact lastly we set our new array into state and set our edit contact id to null as we are finished editing and this will hide the editable row we will say set contacts to be the new contacts array that we just created with the updated contact object and then we'll do set edit contact id we know so if we try and edit only frank and we will just add some stuff in here so we can see the data has changed so we've just updated these inputs and if we click save and it didn't work so let's have a look and see what we missed so if we jump into our function oh it's saying handle edit form submit is declared but never used so we forgot to add our function to our form so we scroll down remember our added contact form wraps the table this is the form we want to add our onto mid to so let's say unsubmit tickle to handle edit form submit which should hopefully fix our error and it does so let's jump back into chrome and try it again so we'll try editing tony prank again the update add some stuff in the rest of these fields and now if i click save you can see that's updated with the new data if we click edit and we try to delete something the form validation should kick in and it does so to finish off this feature let's add a cancel button so in our finished app if we make some changes and then decide that we don't want to change this contact anymore we just hit cancel and the changes are not saved so to cancel all we want to do is set the edit contact id back to no remember the editable row only renders if the edit contact id matches any given contact id in our contacts array otherwise the read-only row is rendered so just above our return function in app.js we will create an event handler for our cancel button so we'll say const handle console click this will be equal to an arrow function and all we have to do in here is say set edit contact id let me know and we will pass this event handler to our editable row that's a prop so we'll say handle cancel click is equal to handle cancel click and in editablerow.js we can destructure this so we'll say handle console click and just below our save button we'll add a new button with a type of button and we'll say cancel and whenever this is clicked we will call our handle cancel click function that we passed in as a prop so now if we try this and if we edit jeremy this time and just add a bunch of stuff in here and if we click cancel you can see that doesn't update the state and it renders the read-only view the final thing we want to look at in this course is how to delete a contact if we look at our finished app we have a delete button and if we click delete it removes that row from the table so we can delete all of these and they all disappear so to achieve this we will remove the row from state and react will re-render the component with the new array which has the given row removed so in app.js we will create a function that gets called when we click a delete button so we'll say const handle delete click and this will be equal to an arrow function which will accept a contact id this id will be used to find the index of that contact and remove them from the contact array so it's similar to what we did with the edit and here we'll say cons new contacts is equal to a copy of the current contacts array so this is similar to what we have done before we're just creating a new array based on the current contacts as to not mutate the state then we will get the index by using the find index method so we'll say cons index equals contacts dot bind index and we'll pass in our arrow function which will get the current contact as a parameter and then we'll say contact dot id is equal to the contact id that was passed in to the function now we have the index next we will use the splice method to remove the contact object at the given index in the array so we'll say v contacts dot splice and the splice array function takes two parameters the index of the item and that array that we want to remove so we'll say index comma one so this just indicates how many elements to remove in this case we want to remove the one and finally we'll see if this interstate so we'll say that contacts and pass in our new contacts so our delete button is going to live in the read-only row component beside the edit button so we will pass this function in as props so we'll say handle delete click is equal to handle delete click like so so in our read-only row we will destructure this by saying handle delete click like so and just beside the edit button or just below the edit button you'll add a new button with a type of button and we will say delete as the button text and we want to pass in our click handler so say on click is equal to handle delete click and we'll pass in our arrow function which calls our handle delete click function and pass in the contact dot id which we get from the contact that was passed in to this component whenever we call a function like this and we pass parameters we must do it within an arrow function as this prevents the function that we're calling to be invoked straight away we do want that to happen we want the function to be called when the click happens so now if we save this and if we open up chrome you can see our delete button has appeared over here if we delete tony you can see he gets removed from the rules if we keep deleting you can see the rules gradually disappear thanks for watching this video if you enjoyed it don't forget to like and subscribe and share this video with your friends thanks for watching
Info
Channel: Chris Blakely
Views: 13,018
Rating: 4.9729729 out of 5
Keywords: create a table in react, create a table in react js, react table tutorial, react table tutorial for beginners, beginners react table tutorial, react table and form tutorial, react project tutorial, react contact list tutorial, react edit table row, react table tutorial from scratch, reactt project for beginners
Id: dYjdzpZv5yc
Channel Id: undefined
Length: 49min 39sec (2979 seconds)
Published: Fri Jun 25 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.