mirror of
https://github.com/dholerobin/Lecture_Notes.git
synced 2025-09-13 13:52:12 +00:00
push
This commit is contained in:
@@ -0,0 +1,400 @@
|
||||
## Agenda
|
||||
|
||||
Today we are going to start with React.
|
||||
|
||||
Prerequisites that you should know:
|
||||
* HTML/CSS
|
||||
* Javascript Core (Understanding of how Javascript works).
|
||||
* DOM (Vanilla JS)
|
||||
* Modularity (We will know how to export and import different functions and properties from the code. While working with React we use lot of Modular code in terms of components. Everything is modular in React, that why we need to understand Modularity. )
|
||||
|
||||
Softwares you must install in System for react
|
||||
* VS Code
|
||||
* NodeJS (React is dependent on NodeJS)
|
||||
* Git (Not necessary)
|
||||
|
||||
Before starting with React, we will first understand 1 topic and then we will move forward with React. That topic is Modularity.
|
||||
|
||||
|
||||
|
||||
whatever code you write shoud be written in form of modules so that you can specifically use, Read and Maintain your Codebase. Modularity refers to the practice of breaking down a larger software program into smaller, self-contained, and reusable modules. These modules encapsulate specific functionality, variables, or objects, making it easier to manage, maintain, and scale your JavaScript code.
|
||||
|
||||
**[Ask the learners]**
|
||||
Suppose you have written all the modules you are using in NodeJS in a single file, is this a Good practice?
|
||||
|
||||
No, this is not a good practice because what modularity says is whatever feature you want to write, make separate module for it.
|
||||
|
||||
Suppose you want to code a calculator and you want to make modules for each function it will do. Just make 4 functions addition, subtraction, multiplication and division.
|
||||
|
||||
We can simply make 4 functions:
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
function add(a, b){
|
||||
console.log(a+b);
|
||||
}
|
||||
|
||||
function sub(a, b){
|
||||
console.log(a+b);
|
||||
}
|
||||
|
||||
function mul(a, b){
|
||||
console.log(a+b);
|
||||
}
|
||||
|
||||
function div(a, b){
|
||||
console.log(a+b);
|
||||
}
|
||||
|
||||
add(2, 3)
|
||||
sub(10, 5)
|
||||
mul(3, 4)
|
||||
div(10, 2)
|
||||
```
|
||||
|
||||
Now we want to make this code modular, and that module, we should be able to use in different JS files. For that we have to import and export functions and properties of the code.
|
||||
|
||||
For exporting the function to new file, first lets create 1 file called ModuleTest.js where we will import all these functions that we have just created.
|
||||
|
||||
for exporting the functions:
|
||||
|
||||
```javascript
|
||||
module.exports={
|
||||
addition: add,
|
||||
substraction : sub,
|
||||
multiplication : mul,
|
||||
division : div
|
||||
}
|
||||
```
|
||||
|
||||
**[Ask the learners]**
|
||||
What would you do if you want to import these files?
|
||||
|
||||
Using Require, yes we can use require to import a file in other JS file.
|
||||
|
||||
So in this case:
|
||||
|
||||
```javascript
|
||||
const calculator = require("./calc")
|
||||
|
||||
calculator.addition(2 , 3)
|
||||
calculator.multiplication(2 ,3)
|
||||
calculator.substraction(3 , 1)
|
||||
calculator.division (10 , 5)
|
||||
```
|
||||
|
||||
So we are trying to import a module named "calc" and use the functions addition, multiplication, subtraction, and division from that module.
|
||||
|
||||
Also we dont need to use console.log here because we are already logging the output in the the function definition.
|
||||
|
||||
Benefits of this are:
|
||||
* Code Reusability: Modules are self-contained units of code that can be reused across your application or in other projects. This reduces the need to rewrite the same functionality, saving time and effort.
|
||||
* Ease of Maintenance: Smaller, well-defined modules are easier to understand and maintain. Developers can focus on individual modules without needing to comprehend the entire codebase, which simplifies debugging and updates.
|
||||
* Readability: Well-structured modules with clear interfaces and documentation enhance code readability. Other developers (including your future self) can easily understand how to use and interact with a module.
|
||||
|
||||
Lets understand module.exports object:
|
||||
|
||||
ModuleExports defines an object in which you can pass your functions in keys and you can use those keys whenever you are Importing your module and they will invoke the function associated with them Respetively.
|
||||
|
||||
It is used to define what a module should export to be used in other parts of your code. It allows you to encapsulate code in a module and expose specific functions, variables, or objects for use in other files or modules.
|
||||
|
||||
So this is all about Modularity.
|
||||
|
||||
Now we will look into React. We will be learning it from scratch.
|
||||
|
||||
|
||||
React is the most popular and important library for Frontend Development. React is most in demand and companies want frontend developers who works on React.
|
||||
|
||||
React is JS library for frontend Development developed By Meta(facebook) and this actually is very fast performing tool and it follows Component Based Architecture.
|
||||
|
||||
Lets understand what is Component Based Architecture:
|
||||
|
||||
If you talk about Vanilla JS, DOM is a tree structure
|
||||
|
||||

|
||||
|
||||
Doing Operations in a DOM is a heavy operations, So remove these Heavy Operations, React introduced Component Based Architecture. It says that anything you create can be hashioned into separate component, allowing you to work on each of them separately.
|
||||
|
||||
**[Ask the learners]**
|
||||
What do I mean by a Component Based Architecture?
|
||||
|
||||
If you take a look at LinkedIn's interface, you'll notice several components, and each of these components functions as an independent entity.If you examine the particular section, you'll see that all the sections are distinct components and also operating of one another.
|
||||
|
||||
This is precisely what React does, it structures the architecture in a component-centric manner.
|
||||
|
||||

|
||||
|
||||
If you currently have an application. React's library will partition each of the sections into distinct components, allowing you to work on them separately and independently.This is essentially the core functionality of React. This makes the code maintainable and it also enables you to reuse individual components repeatedly.
|
||||
|
||||
Consider LinkedIn as an example. You'll notice that various sections, such as the boxes, share a similar structure, even though they contain different data.
|
||||
|
||||
|
||||
So let's create HTML file, index.html to do one task that is append Hello from HTML in an HTML page.
|
||||
|
||||
```javascript=
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
<h1>Hello From HTML</h1>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
We have created the boilerplate code. Now create a div with an ID root. Inside this root element, you can simply include an `<h1>` tag with the message "Hello from HTML."
|
||||
|
||||
**[Ask the learners]**
|
||||
Your task is to display this message "Hello From JS" by using Vanilla JavaScript.
|
||||
|
||||
For doing that you want to create a script
|
||||
|
||||
```javascript=
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let greeting = document.createElement('h1')
|
||||
|
||||
greeting.innerHTML = 'Hello from JS'
|
||||
|
||||
let root = document.getElementById('root')
|
||||
|
||||
root.appendChild(greeting)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Now, what we will be doing is we will be doing the same thing using React. We will understand how React actually handles all of these things, right? Alright, so now let's move on to React. Before delving into React, we have already discussed what React is and its architecture, among other things.
|
||||
|
||||
Let's move to our browser and I'll just type React over here.
|
||||
https://legacy.reactjs.org/docs/getting-started.html
|
||||
So this is the link that you need to follow. So just open this link and you will see this particular website.
|
||||
|
||||
React is not inbuilt in your browser, right? So, first let me show you how you can use React with using CDN links. We'll understand this. Then we'll see what is the problem when we'll use React with CDN links. And then we'll move to creating a new React app.
|
||||
|
||||
As you will go to these CDN links, you will see all of these CDN scripts. You can just copy this and you can paste it in your HTML file.
|
||||
|
||||
```htmlembedded
|
||||
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
||||
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
||||
```
|
||||
|
||||
**[Ask the learners]**
|
||||
Now you must be wondering that why not just create a single link and you can input the React properties and Dom over there as well? Now why not to do that?
|
||||
|
||||
It's all about modularity. React is designed as separate modules because it serves as a comprehensive cross-platform framework, or I should say library, right? This means that in addition to React, we have React Native for mobile development. Furthermore, with React, we can create progressive web apps that can function across various platforms, including laptops, iOS, Android, and any other screen or device you might need, right?
|
||||
|
||||
So basically, to have all of those particular features in different particular links, we have these as separate modules. So these modules can be used anywhere. So that's why we have these two separate modules.
|
||||
|
||||
you'll have to inject these CDN links in your particular application. Only then you'll be able to use React. If you're not injecting these CDN links, you'll not be able to use React.
|
||||
|
||||
if I go to my browser and open console if I type React, now I'm able to access React and React Dom. And now you see if I press Enter, so we have an object. So whole react is just under an object. So now if I open this object, you will see different properties and different methods.
|
||||
|
||||
|
||||
> Note to instructor - Now explain everything in that Object.
|
||||
|
||||
So now we'll move to our VS Code and We'll write some code
|
||||
|
||||
|
||||
|
||||
Thing that you must remember is whenever you are writing React code, just make sure that whenever you start writing React code, these CDN links should be at the top of the code.
|
||||
|
||||
Now let's see how we can use React.createElement() method by creating an element using this.
|
||||
|
||||
> Note to instructor - Explain code at each line.
|
||||
|
||||
```htmlembedded
|
||||
<script>
|
||||
let greeting = React.createElement ('h1' , {} , 'Hello From React')
|
||||
console.log(greeting)
|
||||
let root = ReactDOM.createRoot(document.getElementById('root'))
|
||||
root.render(greeting)
|
||||
</script>
|
||||
```
|
||||
|
||||
The greeting element has been created from the render method. If I do not render this, then there will be no element inside the div.
|
||||
|
||||
If we want to pass attributes to the element that we want to create (let's say ID):
|
||||
|
||||
```htmlembedded
|
||||
<script>
|
||||
let greeting = React.createElement ('h1' , {id:'greeting', className:'greet'} , 'Hello From React')
|
||||
console.log(greeting)
|
||||
let root = ReactDOM.createRoot(document.getElementById('root'))
|
||||
root.render(greeting)
|
||||
</script>
|
||||
```
|
||||
So this is how basically, you can pass on your attributes if you want.
|
||||
|
||||
We have seen this that how to use create element, how to use create root, how to get your root, how to basically use the render method. We have done with all of these things.
|
||||
|
||||
Now Suppose I have an HTML Structure:
|
||||
|
||||
```htmlembedded
|
||||
<div id='parent'>
|
||||
<div id='child'>
|
||||
<h1 id='content'>Hello from react</h1>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
**[Ask the learners]**
|
||||
Now, how can we create a similar Sturture, so can we create it?
|
||||
|
||||
You can follow this code
|
||||
|
||||
```javascript
|
||||
let greeting = React.createElement(
|
||||
"div",
|
||||
{ id: "parent" },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ id: "child" },
|
||||
React.createElement("h1", { id: "content" }, "Hello from React")
|
||||
)
|
||||
);
|
||||
|
||||
```
|
||||
React.createElement("div", { id: "parent" }, ...) - This line creates a React element representing a ``<div>`` with an id attribute of "parent." The ... indicates that there are child elements inside this ``<div>``.
|
||||
React.createElement("div", { id: "child" }, ...) - Inside the "parent" ``<div>``, another React element is created, representing a nested ``<div>`` with an id attribute of "child." Again, the ... indicates that there are child elements inside this nested ``<div>``.
|
||||
React.createElement("h1", { id: "content" }, "Hello from React") - Inside the "child" ``<div>``, there's an h1 element with an id attribute of "content" and the text content "Hello from React."
|
||||
|
||||
You can clearly say that this should not be the way of writing a code, right? And to basically solve this problem, to solve this mess, to solve this hassle, we use something known as JSX and we'll be talking about JSX just after this.
|
||||
|
||||
Before we move forward, lets take a 1 more question.
|
||||
|
||||
**[Ask the learners]**
|
||||
Now, If I want to add 1 more element in the same structure we have created, so can we create it?
|
||||
|
||||
```htmlembedded
|
||||
<div id='parent'>
|
||||
<div id='child'>
|
||||
<h1 id='greeting'>Hello from react</h1>
|
||||
<h2 id='greeting2'>Hello from react Heading 2</h1>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
Now if we see we have two elements in same hierarchical order.
|
||||
|
||||
Here we can use an array.
|
||||
|
||||
```javascript
|
||||
let greeting = React.createElement(
|
||||
"div",
|
||||
{ id: "parent" },
|
||||
React.createElement(
|
||||
"div",
|
||||
{ id: "child" },
|
||||
[React.createElement("h1", { id: "greeting" }, "Hello from React"),
|
||||
React.createElement("h2", { id: "greeting2" }, "Hello from React Heading 2")]
|
||||
)
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
So you can use an array and you can use these methods as each element of the Array.
|
||||
|
||||
But yes, this way of writing code is not recommended at all. This is very bad code. This should not be used. But for your knowledge, you must know all of these things.
|
||||
|
||||
|
||||
|
||||
I'll just create a folder and just call this as demo. So I've created a folder as demo app and in this particular folder we'll be initializing our react application.
|
||||
|
||||
We will now go to https://legacy.reactjs.org/docs/create-a-new-react-app.html and we'll be using Create React app moving forward .
|
||||
|
||||
If you want to use CDN, you'll have to make use of the Dom. You'll have to write all of these script tags, you'll have to go create different JavaScript files again and again and again. That is not recommended at all. So that's why we'll be using Create React app which will actually give you a whole boilerplate code of React and there you'll be able to work with each and everything that React has to offer.
|
||||
|
||||
**Ways To Initialize a React App**
|
||||
* CDN Links: we talked about
|
||||
* CRA (Create React App)
|
||||
* Vite
|
||||
|
||||
We can use The following comamnd to make a React App:
|
||||
|
||||
```javascript
|
||||
npx create-react-app demo
|
||||
```
|
||||
npx means node package executor, So now, if you see, this has started installing all of the things that we'll need. So I want every one of you to do this once. And after you have done, you will see all of these files that will be available inside this demo folder.
|
||||
|
||||
Let's move forward. So I'll just go to the Vs code and I'll just close all of these files my app is running.
|
||||
|
||||
Now you can see that here inside this demo folder, you have all of these files and folders.
|
||||
* There is one folder as Node modules,
|
||||
* There is one public folder.
|
||||
* There's one source folder,
|
||||
* There's a git ignore file
|
||||
* There are two JSON files.
|
||||
* There is one README.
|
||||
|
||||
All of these things are here inside this demo folder.
|
||||
|
||||
So first, let's start with the public folder. Let's start with the public. So let's go inside this public and ignore all of these things. They are all logos and favicons. You do not need to worry about this.
|
||||
|
||||
So if you go to this index.html file. So now you can see this is all your HTML code. This is all your HTML code. And here you have your root.
|
||||
|
||||

|
||||
|
||||
So if you remember, just a while ago, we were doing this thing that everything that you were creating was going inside the root div only. Was going inside the root div only. So this is basically the starting point of your react app. Everything that you will do will go inside this root only, right? So this is that root.
|
||||
|
||||
So whenever you write react code and you will render all of those code, those code will be converted into elements and all of those elements will be created inside this root.
|
||||
|
||||
now if you see if you are clear with this root, now we'll go to the source folder and we'll see where this root has been selected.
|
||||
|
||||
Let's go to index.js and if you go to index.js, you see that there is a line that is actually getting the ID root.
|
||||

|
||||
|
||||
|
||||
Third thing that you must pay your attention to is this particular render method.
|
||||
|
||||

|
||||
|
||||
This says App. This App.js will be rendered inside the root, and this root will have all your HTML elements. I mean, render will convert this app JS file in the form of elements, and all of those elements will be rendered inside your root and you will have your HTML
|
||||
|
||||
I'll be deleting some files from this source folder as well and make sure that you delete them very carefully, because if you delete any important file, your app will be destroyed.
|
||||
|
||||
We can delete reportWebVitals, setupTest, appTest.js, logo.svg. These are the four files that are not needed right now.
|
||||
|
||||
As we have deleted reportWebVitals file, so we need to remove it from index.js else it will cause error.
|
||||
|
||||

|
||||
|
||||
In app.js remove logo.svg.
|
||||
|
||||
Basically there's one command which is `npm start`.
|
||||
|
||||
Make sure you're on the same folder where your react app is. So it is inside this demo folder. So here you just have to write `npm start` and you just need to press Enter.
|
||||
|
||||
App has been started on localhost:3000.
|
||||
|
||||
We can update our app.js file to show some content:
|
||||
|
||||

|
||||
|
||||
We are creating a function that is JavaScript. Whenever you create a function that is JavaScript inside this, you are returning a div which is HTML. So basically, when you use JavaScript and HTML together, that thing is known as JSX. The full form of JSX is JavaScript XML. You can just say that JSX is when you use JavaScript and HTML together, that particular thing is JSX.
|
||||
|
||||
And this kind of functions, this kind of functions are known as functional component. This is a functional component.
|
||||
|
||||
But for today, I would like to stop over here only. I don't want to confuse you more. I don't want to go in more depth today because we have already seen a lot of stuff today. So this was an introductory class to React, in which we understood that what react is.
|
||||
|
||||
So in the next class, we'll be seeing how components can be created. What are states, what are props, how can you work with props? And how can you basically work along with react?
|
||||
|
@@ -0,0 +1,408 @@
|
||||
# Asynchronous Redux and Thunk-2
|
||||
|
||||
## Agenda
|
||||
* Learn about Redux Thunk.
|
||||
* Introduction to Redux life cycle.
|
||||
* Introduction to Redux thunk life cycle.
|
||||
* Modify the code done in the previous class according to the concepts taught in today's class.
|
||||
|
||||
|
||||
## Redux
|
||||
> Give a quick walk through of the previous code and the webpage designed till now.
|
||||
> Revise the concepts taught in the previous class in brief.
|
||||
|
||||
* The principle of Redux is single source of truth.
|
||||
* The state changes that are done in an application are governed by the **store**.
|
||||
* Store will have the reducers that are required to manipulate the data.
|
||||
|
||||
Inside our products component, we are fetching our data from the below code. We use the `useEffect` hook and use `axios`.
|
||||
|
||||
```javascript
|
||||
useEffect(()=>{
|
||||
const getProducts = async ()=>{
|
||||
const res = await axios.get('https://fakestoreapi.com/products')
|
||||
console.log(res.data)
|
||||
setProducts(res.data)
|
||||
}
|
||||
```
|
||||
|
||||
But, the data inside `useEffect` hook has to be accessed by multiple components.
|
||||
|
||||
> ASk the students, if it is a good way to define the data using `useEffect` hook for every use case? The answer is No.
|
||||
|
||||
* We can use a **Redux store** to perform these operations.
|
||||
* We can do asynchronous tasks in Redux using **Redux Thunk**.
|
||||
|
||||
>Create a file called `productSlice.js` inside the `store` folder.
|
||||
|
||||
In `productSlice.js`, follow these steps.
|
||||
|
||||
**Step 1**- Import **`createSlice`** from redux toolkit.
|
||||
|
||||
```javascript
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
```
|
||||
|
||||
**Step 2**- We are creating a slice object `productSlice`. Inside that, there is `initialState` where we can have multiple options in the form of an objects. It is so because the API may not return success every time. There might be failures or other things received from an API.
|
||||
|
||||
```javascript
|
||||
initialState:{
|
||||
}
|
||||
```
|
||||
|
||||
**Step 3**- The slice has the first parameter as the name which is `Product`. The `initialState` will be an object. Inside the `initialState`, the first key will be `data` which will be an array. The second key will be `status` which will show the status of page loading.
|
||||
```javascript
|
||||
const productSlice=createSlice({
|
||||
name:'Product',
|
||||
initialState:{
|
||||
data:[],
|
||||
status:STATUSES.SUCCESS
|
||||
},
|
||||
```
|
||||
|
||||
**Step 4**- We will define the different possibilities of `status` outside the `productSlice object`. There can be 3 possibilities-
|
||||
* **Loading**- You are calling an API and it is loading.
|
||||
* **Success**- The API has loaded successfully.
|
||||
* **Error**- The API has encountered an error.
|
||||
|
||||
```javascript
|
||||
const STATUSES={
|
||||
SUCCESS:'success',
|
||||
ERROR:'error',
|
||||
LOADING:'loading'
|
||||
}
|
||||
```
|
||||
It is a good practice to have the word `STATUSES` in capitals.
|
||||
|
||||
The value of `status` inside our `initialState` object will be SUCCESS.
|
||||
|
||||
```javascript
|
||||
initialState:{
|
||||
data:[],
|
||||
status:STATUSES.SUCCESS
|
||||
}
|
||||
```
|
||||
|
||||
**Step 5**- We will now create reducers. This reducer will be setting a product and hence will be named as `setProduct`. It takes in two parameters- `state` and `action`. Now we will define the reducer, i.e, what it will do. The data that it will receive will be a payload entirely.
|
||||
|
||||
```javascript
|
||||
reducers: {
|
||||
setProducts(state,action){
|
||||
state.data=action.payload
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 6**- To get the data, we will have to use **Thunks**. Thunks are generally functions only but they take asynchronous functions inside them and they process the reducers.
|
||||
|
||||
As thunks are nothing but functions, we will use the `function` keyword to define them. It returns an `async function` and a parameter names `dispatch`.
|
||||
|
||||
>Tell the students that there can be multiple possibilities of status which can also lead to an error. How can they be handled? Ans is by using a try and catch block.
|
||||
|
||||
**Step 7**- In the try block, we will get the data. The data is fetched from
|
||||
|
||||
```javascript
|
||||
const res= await axios.get('https://fakestoreapi.com/products')
|
||||
```
|
||||
|
||||
|
||||
**Key points**
|
||||
* The reason to use React Thunks is- The API calls are heavy and they take time.
|
||||
* Whenever something takes a lot of time in JavaScript, that is tackled using asynchronous way.
|
||||
|
||||
The data needs to be dispatched to our reducer. First we need to export the `setProduct` reducer.
|
||||
|
||||
```javascript
|
||||
export const {setProducts}=productSlice.actions
|
||||
export default productSlice.reducer
|
||||
```
|
||||
Then we will add this line inside our try block.
|
||||
|
||||
```javascript!
|
||||
dispatch(setProducts(res.data))
|
||||
```
|
||||
|
||||
**Step 8**- The 2nd reducer is `setStatus`.
|
||||
|
||||
```javascript!
|
||||
setStatus(state,action){
|
||||
state.status=action.payload
|
||||
}
|
||||
```
|
||||
|
||||
First, the data is in the loading state, the status can be set as loading. It is written before the try block.
|
||||
|
||||
```javascript
|
||||
dispatch(setStatus(STATUSES.LOADING))
|
||||
```
|
||||
Then, after the data is fetched, success status is returned. It is inside the try block after the data is dispatched.
|
||||
|
||||
```javascript
|
||||
dispatch(setStatus(STATUSES.SUCCESS))
|
||||
```
|
||||
|
||||
In the catch block, the error is caught.
|
||||
```javascript
|
||||
dispatch(setStatus(STATUSES.ERROR))
|
||||
```
|
||||
|
||||
The function `fetchProducts` will be exported using the `export` keyword. Using it, we can use it anywhere in our code.
|
||||
|
||||
The code till now is
|
||||
|
||||
```javascript!
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import axios from 'axios';
|
||||
|
||||
const STATUSES={
|
||||
SUCCESS:'success',
|
||||
ERROR:'error',
|
||||
LOADING:'loading'
|
||||
}
|
||||
|
||||
const productSlice=createSlice({
|
||||
name:'Product',
|
||||
initialState:{
|
||||
data:[],
|
||||
status:STATUSES.SUCCESS
|
||||
},
|
||||
reducers: {
|
||||
setProducts(state,action){
|
||||
state.data=action.payload
|
||||
}
|
||||
},
|
||||
setStatus(state,action){
|
||||
state.status=action.payload
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
export const {setProducts,setStatus}=productSlice.actions
|
||||
export default productSlice.reducer
|
||||
|
||||
export function fetchProducts(){
|
||||
return async function fetchProductThunk(dispatch){
|
||||
dispatch(setStatus(STATUSES.LOADING))
|
||||
try{
|
||||
const res= await axios.get('https://fakestoreapi.com/products')
|
||||
dispatch(setProducts(res.data))
|
||||
dispatch(setStatus(STATUSES.SUCCESS))
|
||||
|
||||
}
|
||||
catch(error){
|
||||
console.log(error)
|
||||
dispatch(setStatus(STATUSES.ERROR))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
Now we will register our `productSlice` in `store.js`. It will be imported in `store.js` file.
|
||||
|
||||
```javascript
|
||||
import productReducer from './productSlice'
|
||||
```
|
||||
|
||||
And we will also use product
|
||||
```javascript
|
||||
const store = configureStore({
|
||||
reducer : {
|
||||
cart : cartReducer,
|
||||
products:productReducer
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Now we will check if our data is being reflected in our app. We will check in the inspect section under redux and refresh the page. We will see one slice.
|
||||
|
||||

|
||||
|
||||
It is the store reducer.
|
||||
|
||||
In our products.js we need to import `fetchProducts`. The code-
|
||||
|
||||
```javascript
|
||||
useEffect(()=>{
|
||||
const getProducts = async ()=>{
|
||||
const res = await axios.get('https://fakestoreapi.com/products')
|
||||
console.log(res.data)
|
||||
setProducts(res.data)
|
||||
}
|
||||
```
|
||||
|
||||
will be replaced with
|
||||
|
||||
```javascript
|
||||
useEffect(()=>{
|
||||
const getProducts = async ()=>{
|
||||
dispatch(fetchProducts())
|
||||
}
|
||||
```
|
||||
|
||||
After dispatching, we will get all of the reducers.
|
||||

|
||||
|
||||
|
||||
Now the state has been updated.
|
||||
>ASk the students which hook will give the access to the whole state? Ans is `useSelector` hook. In Products.js, modify the import hook line.
|
||||
|
||||
```
|
||||
import {useDispatch,useSelector} from 'react-redux'
|
||||
```
|
||||
|
||||
We will need the product state as we are working on the products now. So, we will say- `useSelector` give me access to the state.
|
||||
|
||||
```javascript
|
||||
const {data:products,status}= useSelector((state)=>state.products)
|
||||
```
|
||||
The names should be used from the store and not slices.
|
||||
|
||||
|
||||
The following lines should be removed from our code. From the Products.js, we will remove the useState hook. We will also remove the import axios line. After saving, the app will look like this-
|
||||
|
||||

|
||||
|
||||
The data here is coming from Redux- Redux thunk.
|
||||
|
||||
> Give a quick recap of productSlice.js code which contains the thunk.
|
||||
|
||||
We are able to maintain any particular state for any component by using just the store.
|
||||
|
||||
### Redux Life Cycle
|
||||
|
||||
> Tell the students that we will be understanding the concepts of Redux by referring to the redux documentation. [Refer this](https://redux.js.org/tutorials/essentials/part-1-overview-concepts).
|
||||
|
||||
After scrolling down, there will be a figure-
|
||||
|
||||

|
||||
|
||||
Now we will understand it by comparing with our app. When we go on our app, it is our UI or User Interface. There are several buttons and thee buttons will **dispatch** actions.
|
||||
|
||||
So, when you click on the button- add to cart, it will trigger an event and it is dispatched as a action.
|
||||
|
||||
Let us see dispatch.
|
||||
|
||||

|
||||
|
||||
* As soon as you dispatch, the action payload will go via the reducer.
|
||||
* The reducer is defined inside the central store.
|
||||
* The reducer will make changes to the state.
|
||||
|
||||

|
||||
|
||||
* After the state has been updated, it will be be visible in the UI.
|
||||
|
||||
**This is the life cycle of Redux**
|
||||
|
||||

|
||||
|
||||
* First step is the UI.
|
||||
* Some event will occur and they will go inside the **event handler**.
|
||||
>Show some examples of event handlers in the code.
|
||||
* That event will be **dispatched** with the **action payload**. The **action** will be dispatched.
|
||||
* The action will be going inside the **reducer**. The reducers are inside the central store where there are multiple reducers.
|
||||
* These reducers will make changes to the state.
|
||||
* The state will make changes to the UI.
|
||||
|
||||
### Redux Thunk life cycle
|
||||
|
||||
Just like Redux life cycle, there is Redux thunk life cycle also. The link is [here](https://redux.js.org/tutorials/essentials/part-5-async-logic).
|
||||
|
||||

|
||||
|
||||
* It will start from UI.
|
||||
* An event will happen and it will go to the event handler.
|
||||
* Action is dispatched here.
|
||||
* Middleware is any function that is present inside the flow of the function. It does a particular task inside a sequence.
|
||||
* As it is async, middleware will send a request to the API. The API will send a response to the middleware.
|
||||
* As soon as the response is received, it will go to the reducer.
|
||||
* So the reducer is making the changes to the state.
|
||||
* The state is making changes to UI.
|
||||
|
||||
> Ask the students to relate the code with the Redux life cycle.
|
||||
|
||||
|
||||
### Redux toolkit
|
||||
This is the old way to create thunk in React.
|
||||
|
||||
```javascript!
|
||||
export function fetchProducts(){
|
||||
return async function fetchProductThunk(dispatch){
|
||||
dispatch(setStatus(STATUSES.LOADING))
|
||||
try{
|
||||
const res= await axios.get('https://fakestoreapi.com/products')
|
||||
dispatch(setProducts(res.data))
|
||||
dispatch(setStatus(STATUSES.SUCCESS))
|
||||
|
||||
}
|
||||
catch(error){
|
||||
console.log(error)
|
||||
dispatch(setStatus(STATUSES.ERROR))
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now as there is Redux toolkit, we can use a method provided to us by the redux toolkit. The method is **`createAsyncThunk`**.
|
||||
|
||||
* First import `createAsyncThunk` at the start.
|
||||
* This method was created to reduce the length of the code and make it more concise.
|
||||
|
||||
```javascript
|
||||
export const fetchProducts=createAsyncThunk('products',async()=>{
|
||||
const res= await axios.get('https://fakestoreapi.com/products')
|
||||
return res.data
|
||||
})
|
||||
```
|
||||
Here, 'products' is the name of the thunk.
|
||||
|
||||
> Ask the students, if they wonder how the code is working if we are not sending any actions to the reducers.
|
||||
|
||||
There is also a change to the reducers part. We will not be defining the reducers in this way
|
||||
|
||||
```javascript
|
||||
reducers: {
|
||||
setProducts(state,action){
|
||||
state.data=action.payload
|
||||
}
|
||||
},
|
||||
setStatus(state,action){
|
||||
state.status=action.payload
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Instead, the code shown below will be used. We will use the `extraReducers` function. This function takes in `builders` as a parameter and within this function, we will define our cases.
|
||||
|
||||
The `builder` is similar to promises and has three cases- pending, fulfilled and rejected.
|
||||
|
||||
```javascript
|
||||
extraReducers : (builder)=>{
|
||||
builder.addCase(fetchProducts.pending, (state)=>{
|
||||
state.status=STATUSES.LOADING
|
||||
}).addCase(fetchProducts.fulfilled, (state,action)=>{
|
||||
state.data=action.payload
|
||||
state.status=STATUSES.SUCCESS
|
||||
}).addCase(fetchProducts.rejected, (state)=>{
|
||||
state.status=STATUSES.LOADING
|
||||
})
|
||||
}
|
||||
```
|
||||
Before running the code, import `fetchProducts` also.
|
||||
|
||||
Depending on the data, the callback function will return the status.
|
||||
|
||||
|
||||
Additionally, in our products.js code, we can also see the current status by including the following
|
||||
|
||||
```javascript
|
||||
if(status===STATUSES.LOADING){
|
||||
return <h2>Loading..</h2>
|
||||
}
|
||||
|
||||
if(status===STATUSES.ERROR){
|
||||
return <h2>Something is wrong</h2>
|
||||
}
|
||||
```
|
@@ -0,0 +1,310 @@
|
||||
# Full Stack LLD & Projects: React-9: React Redux Interview Question 1
|
||||
|
||||
|
||||
We will discuss some Advanced hooks which are generally asked in React Interview and are very Important to optimize your react
|
||||
|
||||
* Use Reducer
|
||||
* Use Transition
|
||||
|
||||
### Explanation
|
||||
|
||||
In this existing project, we have made a TODO list application where if we enter the task and date, it will create a TODO task, and if we clicke on the tick option, it will mark the task as done by striking off the task.
|
||||
|
||||
To generate random task-ids, we have used **react-uuid** library, which can be installed easily by `npm install` command.
|
||||
|
||||
```javascript
|
||||
const handleTask = (e) => {
|
||||
e.preventDefault();
|
||||
const key = e.target.name;
|
||||
const value = e.target.value;
|
||||
setTask({...task, [key]: value});
|
||||
}
|
||||
```
|
||||
|
||||
This above code handles user input in a TODO list project. It prevents default form submission, extracts input field name and value, and updates the task state, likely representing a new task's properties like name and description. This is a key part of adding tasks to the TODO list.
|
||||
|
||||
|
||||
```javascript
|
||||
const addTask = () => {
|
||||
console.log(task);
|
||||
console.log(uuid());
|
||||
const updated = {...task, "id": uuid(), "isDone": false}
|
||||
console.log(updated);
|
||||
setList([...list, updated]);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
This above code defines an `addTask` function that logs the current task state, generates a unique ID using `uuid()`, creates an updated task object with an ID and a default "isDone" status of false, logs the updated task, and finally updates the list state by appending the updated task to it. This function is typically used to add a new task to a TODO list.
|
||||
|
||||
```javascript
|
||||
const markDone = (id) => {
|
||||
console.log(`Task with ${id} is done!`);
|
||||
const index = list.findIndex((task) => task.id === id);
|
||||
const doneTask = [...list];
|
||||
doneTask[index].isDone = true;
|
||||
setList(doneTask);
|
||||
}
|
||||
```
|
||||
|
||||
This above code defines a `markDone` function that takes an id as a parameter. It logs a message indicating that a task with the specified id is marked as done. It then finds the index of the task with that id in the list array using `findIndex`, creates a copy of the list, updates the isDone property of the found task to true, and finally updates the list state with the modified task. This function is typically used to mark a task as completed in a TODO list.
|
||||
|
||||
```javascript
|
||||
const deleteTask = (id) => {
|
||||
console.log(`Task with ${id} to remove!`)
|
||||
const filteredTask = list.filter((task) => task.id !== id);
|
||||
setList([...filteredTask]);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
This code defines a `deleteTask` function that takes an id as a parameter. It logs a message indicating that a task with the specified id is being removed. It then creates a new array filteredTask by filtering out the task with the given id from the list. Finally, it updates the list state with the filtered array, effectively removing the task with the specified id. This function is typically used to delete a task from a TODO list.
|
||||
|
||||
|
||||

|
||||
|
||||
### Explanation
|
||||
|
||||
As we can see, currently we are changing one particular state (ie setList) using three different functions.
|
||||
|
||||
In larger projects, managing state changes across multiple functions can lead to increased complexity, potential inconsistencies, and difficulties in debugging, making centralized state management solutions like Redux or context API more valuable for maintaining code quality and scalability.
|
||||
|
||||
So for this we can use **useReducer** hook.
|
||||
|
||||
Using the useReducer hook can be a beneficial approach to centralize state management in larger projects. It provides a structured way to manage complex state logic, reduce code duplication, and maintain consistency in state updates, making it a suitable choice for maintaining state in more organized and scalable manner.
|
||||
|
||||

|
||||
|
||||
**Without useReducer:**
|
||||
|
||||
* **Events:** User interacts with the UI, triggering various events (e.g., clicks, inputs).
|
||||
* **State:** Multiple state variables are scattered throughout the component to track different aspects of the application's data.
|
||||
* **Updates:** Events directly modify these state variables, potentially leading to scattered and uncoordinated state changes.
|
||||
|
||||
**With useReducer:**
|
||||
|
||||
* **Events:** User interactions still trigger events as before.
|
||||
* **Reducer:** A centralized reducer function defines how state changes in response to different actions.
|
||||
* **Dispatch:** Events are dispatched with specific actions to the reducer.
|
||||
* **State:** The reducer updates the state based on the action and returns a new state object.
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### Explanation
|
||||
|
||||
**Syntax:**
|
||||
|
||||
```javascript
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
```
|
||||
|
||||
**Anatomoy:**
|
||||
|
||||
* **State:** This represents the current state of your data, similar to state variables in `useState`. It's what you want to manage or update.
|
||||
* **Initial State:** The initial value for your state before any actions are applied. It's specified when you call useReducer, like `useReducer(reducerFunction, initialState)`.
|
||||
* **Dispatch Method:** A function provided by `useReducer` that you use to send actions to the reducer. Actions are objects that describe what should happen to the state.
|
||||
* **Actions:** These are plain JavaScript objects that contain a type field (a string identifying the action) and optionally, a payload field carrying additional data. Actions describe what specific changes should be made to the state.
|
||||
|
||||
|
||||
|
||||
### Explanation
|
||||
|
||||
We will be now modifying our existing TODO list project using `useReducer`.
|
||||
|
||||
We will create a file `reducers.js` and start implementing the script for reducer.
|
||||
|
||||
```javascript
|
||||
const taskReducers = (state , action) =>{
|
||||
switch(action.type){
|
||||
case "ADD_TASK" : {
|
||||
const newTask = {...action.payload , "id" : uuid() , "isDone": false }
|
||||
return [...state , newTask]
|
||||
|
||||
}
|
||||
|
||||
case "REMOVE_TASK" : {
|
||||
const taskRemained = state.filter((task)=> task.id !== action.payload )
|
||||
return [...taskRemained]
|
||||
|
||||
}
|
||||
|
||||
case "DONE_TASK" : {
|
||||
const index = state.findIndex((task) => task.id === action.payload);
|
||||
const doneTask = [...state];
|
||||
doneTask[index].isDone = true;
|
||||
return [...doneTask]
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here's an explanation of each reducer case in the `taskReducers` function:
|
||||
|
||||
1. **ADD_TASK:**
|
||||
|
||||
* When an action of type "ADD_TASK" is dispatched, it creates a new task object by spreading the payload (which should contain task details).
|
||||
* It adds an "id" to the new task using a unique identifier (e.g., `uuid()`).
|
||||
* It sets the "isDone" property to false for the new task.
|
||||
* Finally, it returns a new state array by adding the new task to the existing state array.
|
||||
|
||||
2. **REMOVE_TASK:**
|
||||
|
||||
* When an action of type "REMOVE_TASK" is dispatched, it filters out the task with the specified ID from the state using `filter()`.
|
||||
* It returns a new state array with the specified task removed.
|
||||
|
||||
|
||||
3. **DONE_TASK:**
|
||||
|
||||
* When an action of type "DONE_TASK" is dispatched, it finds the index of the task with the specified ID in the state using `findIndex()`.
|
||||
* It creates a copy of the state array.
|
||||
* It updates the "isDone" property of the task at the found index to true.
|
||||
* Finally, it returns a new state array with the updated task.
|
||||
|
||||
Similarly we will create reducers for other functions like `handleTask`.
|
||||
|
||||
```javascript
|
||||
const formReducers = (state , action) => {
|
||||
switch(action.type){
|
||||
case 'HANDLE_TASK' : {
|
||||
return {
|
||||
...state,
|
||||
[action.field] : action.payload
|
||||
}
|
||||
}
|
||||
|
||||
default :
|
||||
return state
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
**HANDLE_TASK:**
|
||||
|
||||
* When an action of type "HANDLE_TASK" is dispatched, it updates the state.
|
||||
* It creates a new state object by spreading the existing state.
|
||||
* It sets a property in the new state object using the action.field as the key and action.payload as the value.
|
||||
* This case is typically used to handle form input changes, where action.field represents the field name (e.g., "title" or "description") and action.payload contains the new value entered by the user.
|
||||
|
||||
Now we will make changes in our TODO List script. Lets name the new script as `TodoReduced.js`.
|
||||
|
||||
```javascript
|
||||
const TodoReduced = () => {
|
||||
|
||||
const [state , dispatch] = useReducer(taskReducers , [])
|
||||
|
||||
const [task , dispatchForm] = useReducer(formReducers ,{
|
||||
title: "",
|
||||
by: ""
|
||||
} )
|
||||
```
|
||||
|
||||
* The first `useReducer` hook initializes the state variable to manage the TODO list items. It uses the taskReducers reducer function and an empty array [] as the initial state. This hook is responsible for managing the list of tasks, including adding, removing, and marking tasks as done.
|
||||
* The second `useReducer` hook initializes the task variable to manage form input fields for creating new tasks. It uses the formReducers reducer function and an initial state object containing two fields: "title" and "by." This hook is responsible for handling changes in the form inputs where users can enter the title and creator of a new task.
|
||||
|
||||
Now we will modify our `handleTask` code.
|
||||
|
||||
```javascript
|
||||
const handleTask = (e) => {
|
||||
e.preventDefault();
|
||||
dispatchForm({
|
||||
type : 'HANDLE_TASK',
|
||||
field : e.target.name,
|
||||
payload : e.target.value
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
* **`e.preventDefault();`:** Prevents the default form submission behavior, ensuring that the page does not refresh when the form is submitted.
|
||||
* **`dispatchForm({ type: 'HANDLE_TASK', field: e.target.name, payload: e.target.value });`:** Dispatches an action to the formReducers reducer. It specifies the action type as "HANDLE_TASK," along with the field (the name of the form input) and the payload (the new value entered by the user). This action is used to update the state related to the form inputs, like the task.
|
||||
|
||||
```javascript
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<h1>My TodoList by using useReducer</h1>
|
||||
<div>
|
||||
I want to do <input type="text" name="title" onChange={handleTask}/> by{" "}
|
||||
<input type="date" name="by" onChange={handleTask} />
|
||||
<button className="wishBtn" onClick={()=> dispatch({type :"ADD_TASK" , payload : task })}>Add a Task</button>
|
||||
</div>
|
||||
<ul>
|
||||
{state && state.length >0 && state.map((item) => (
|
||||
<li key={item.id}>
|
||||
<span style={{ textDecoration: item.isDone ? "line-through" : "" }}>
|
||||
<strong>{item.title}</strong> is due by {item.by}</span>
|
||||
<span><TiTick size={24} onClick={() => dispatch({type:"DONE_TASK" , payload :item.id})} /></span>
|
||||
<span><TiTrash size={24} onClick={() => dispatch({type : "REMOVE_TASK" , payload : item.id})}/></span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
* It renders the TODO list component, which includes an input field for the task title, an input field for the task due date, and a "Add a Task" button.
|
||||
* The `onChange` event handlers (handleTask) are used to capture changes in the input fields and update the form-related state.
|
||||
* It maps over the `state` array (representing the list of tasks) and displays each task with its title and due date.
|
||||
* If a task is marked as done, it displays a line-through style, and there are icons to mark a task as done or delete it. These actions (`DONE_TASK` and `REMOVE_TASK`) are dispatched to update the list of tasks in the state.
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
We will create a component which will take input, and print the input we gave.
|
||||
|
||||
```javascript
|
||||
const List = () => {
|
||||
|
||||
const [input, setInput] = useState('')
|
||||
const [list, setList] = useState([])
|
||||
|
||||
const [isPending , startTransition] = useTransition()
|
||||
|
||||
const LIST_SIZE = 11000
|
||||
|
||||
|
||||
function handleChange(e) {
|
||||
setInput(e.target.value)
|
||||
|
||||
startTransition(()=>{
|
||||
const newList = []
|
||||
for (let i = 0; i < LIST_SIZE; i++) {
|
||||
newList.push(e.target.value)
|
||||
}
|
||||
|
||||
setList(newList)
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
|
||||
<input type='text' value={input} onChange={handleChange}></input>
|
||||
{
|
||||
isPending? <div> Loadingg..</div> : list.map((item) => {
|
||||
return <div>{item}</div>
|
||||
})
|
||||
}
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
This component, named `List`, renders an input field that captures user `input`. When the user types, it updates the input state. However, it also utilizes `startTransition` from the `useTransition` hook to optimize rendering for a large list (specified by `LIST_SIZE`). While `input` is being processed, it displays "Loading..." to improve user experience. After processing, it maps over the list state and displays the input value multiple times based on `LIST_SIZE`.
|
||||
|
||||
|
||||
|
||||
---
|
@@ -0,0 +1,246 @@
|
||||
## Full Stack LLD & Projects: React -10: React Redux Interview Question 2
|
||||
|
||||
|
||||
**Agenda of this lecture:**
|
||||
|
||||
* Code splitting
|
||||
* Lazy loading
|
||||
* Higher order components
|
||||
* Controlled and Uncontrolled components
|
||||
|
||||
|
||||
|
||||
### Explanation
|
||||
|
||||
Before jumping into code splitting we will first understand the concept of **bundling** in react.
|
||||
|
||||
A React application can have multiple components, each spread across different JavaScript files. When you build the application, you want to bundle these separate files into a single file for optimized delivery to the browser. Webpack is a popular tool used for this purpose. It takes all your application's code, along with its dependencies, and packages them into a single bundle file, which is efficient for loading and executing in the browser.
|
||||
|
||||

|
||||
|
||||
Webpack helps with bundling in a React application by efficiently combining multiple JavaScript files and assets into a single bundle. It optimizes resource delivery, reduces HTTP requests, and enhances the application's loading performance.
|
||||
|
||||
### Code splitting
|
||||
|
||||
Code splitting is a technique that allows a React application to break its code into smaller, separate files or "chunks." Instead of loading the entire application code upfront, only the necessary code is loaded when a specific feature or route is accessed. This improves initial loading times and resource efficiency, enhancing the overall user experience.
|
||||
|
||||
Code splitting is like fetching ingredients from the fridge only when you need them to make a sandwich. In React, it means loading parts of your application's code on-demand, improving performance by reducing initial load times and resource usage.
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
First lets see how data is split and then we will see how react components can be split:-
|
||||
|
||||
**Splitting of Data:**
|
||||
|
||||
|
||||
The `data.js` looks like this:-
|
||||
|
||||
```javascript
|
||||
export const moviesData = [{
|
||||
id : '1',
|
||||
name : 'Mission Impossible : Dead reckoning'
|
||||
},
|
||||
{
|
||||
id : '2',
|
||||
name : 'Oppenhiemer'
|
||||
},
|
||||
{
|
||||
id : '3',
|
||||
name : 'Barbie'
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
When you bundle your code, it combines all the imported modules, like data.js, into one optimized file. This makes it easier to manage and deploy your application because you only need to serve a single bundle file to the client browser.
|
||||
|
||||
In this the data is split using **dynamic importing** in JavaScript. Instead of loading the movie data immediately, the `getMovies` function uses the `import()` function to fetch the data from a separate file called data.js when needed. This is a form of code splitting, as it loads the data module only when required, which can help optimize performance by reducing the initial load time of your application.
|
||||
|
||||
|
||||
### Explanation
|
||||
|
||||
Bundling of react components is done using **Lazy Loading**.
|
||||
|
||||
React components are bundled using Lazy Loading. Lazy Loading is a technique that allows you to load components only when they are needed, rather than upfront when the application initially loads.
|
||||
|
||||
Here's how it works:
|
||||
|
||||
* Each component (`Home`, `Products`, `Testimonials`, and `About`) is imported using the `lazy()` function, which dynamically imports the component files when they are required.
|
||||
* This means that the code for these components is split into separate chunks and loaded on-demand, reducing the initial load time of your application.
|
||||
* Lazy Loading helps optimize performance by loading only the necessary components when the user navigates to specific routes, improving the user experience and minimizing unnecessary network requests.
|
||||
|
||||
```javascript
|
||||
const Home = lazy(()=> import('./pages/Home'))
|
||||
const Products = lazy(()=> import('./pages/Products'))
|
||||
const Testimonials = lazy(()=> import('./pages/Testimonials'))
|
||||
const About = lazy(()=> import('./pages/About'))
|
||||
```
|
||||
|
||||
Now we will wrap the routes:-
|
||||
|
||||
The lazy loading of components is related to how routes are wrapped using React's Suspense and React Router's Routes components.
|
||||
|
||||
the lazy loading of components is related to how routes are wrapped using React's Suspense and React Router's Routes components.
|
||||
|
||||
* **Suspense:** The `<Suspense>` component is used to specify a fallback UI (in this case, `<h2>Loading...</h2>`) while waiting for dynamically imported components to load. It's a key part of lazy loading as it ensures a smooth user experience by displaying a loading indicator until the requested component is ready.
|
||||
* **Routes:** Inside the `<Routes>` component, various routes are defined, and each route specifies a component to render when the corresponding URL is matched. These components, such as `<Home />`, `<About />`, `<Products />`, and `<Testimonials />`, are dynamically imported using lazy loading. This means that the components will only be loaded from the server when their respective routes are accessed by the user.
|
||||
|
||||
```javascript
|
||||
return (
|
||||
<div className="App">
|
||||
{/* <h1>These are the Movies</h1>
|
||||
<button onClick={getMovies}>Show Movies</button>
|
||||
<p>{movies.length>0 ? JSON.stringify(movies) : ''}</p> */}
|
||||
|
||||
{/* <Suspense fallback={<h2>Loading...</h2>}>
|
||||
<BrowserRouter>
|
||||
|
||||
<Navbar/>
|
||||
|
||||
<Routes>
|
||||
|
||||
<Route path='/' element={<Home/>}/>
|
||||
<Route path='/about' element={<About/>}/>
|
||||
<Route path='/products' element={<Products/>}/>
|
||||
<Route path='/testimonials' element={<Testimonials/>}/>
|
||||
</Routes>
|
||||
|
||||
|
||||
</BrowserRouter>
|
||||
</Suspense> */}
|
||||
|
||||
{/* <CompA/>
|
||||
<CompA dark />
|
||||
<CompA yellow/> */}
|
||||
|
||||
|
||||
<Controlled/>
|
||||
<Uncontrolled/>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
### Explanation
|
||||
|
||||
|
||||
High Order Components (HOCs) are a design pattern in React that allows you to reuse component logic and share it among multiple components. HOCs are not components themselves but functions that take a component and return a new enhanced component with additional behavior. They are a way to abstract common functionality from components and promote code reusability.
|
||||
|
||||
Let's use three components, A, B, and C, each with different features, to illustrate the concept:
|
||||
|
||||
1. Component A (3 features)
|
||||
Component A is a base component with three specific features: FeatureX, FeatureY, and FeatureZ.
|
||||
Component B (4 features)
|
||||
2. Component B also requires the same three features as Component A (FeatureX, FeatureY, FeatureZ) and an additional feature, FeatureW.
|
||||
Component C (5 features)
|
||||
3. Component C needs the same three features as Components A and B (FeatureX, FeatureY, FeatureZ), plus two extra features, FeatureV and FeatureU.
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
|
||||
|
||||
const styles = {
|
||||
dark : {
|
||||
background : 'black',
|
||||
color : 'white'
|
||||
} ,
|
||||
|
||||
yellow : {
|
||||
background : "yellow",
|
||||
color : 'red'
|
||||
}
|
||||
}
|
||||
|
||||
function HOC(WrappedComp) {
|
||||
|
||||
// args = [dark ,yellow]
|
||||
return function (args){
|
||||
let temp ={}
|
||||
|
||||
if(args.dark){
|
||||
temp = {...styles.dark}
|
||||
}
|
||||
|
||||
else if (args.yellow){
|
||||
temp = {...styles.yellow}
|
||||
}
|
||||
|
||||
let obj = {...args , style : temp}
|
||||
|
||||
return <WrappedComp {...obj}/>
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default HOC
|
||||
```
|
||||
|
||||
This code defines a Higher Order Component (HOC) that takes another component, WrappedComp, as an argument and returns an enhanced version of it with additional styling based on the provided arguments (dark or yellow).
|
||||
|
||||
* The styles object contains predefined styles for both 'dark' and 'yellow' modes.
|
||||
* The HOC function takes `WrappedComp` as an argument and returns a new function.
|
||||
* Inside this function, it checks whether the args object contains a 'dark' or 'yellow' property.
|
||||
* Depending on the property found, it applies the corresponding styles to temp.
|
||||
* The style property is added to the args object with the selected styles.
|
||||
* Finally, the `WrappedComp` is rendered with the updated args containing the chosen styles.
|
||||
|
||||
|
||||
### Explanation
|
||||
|
||||
**Controlled Components:** Controlled components are React components where the value of an input element is controlled by the component's state. In this given below example, the `Controlled` component maintains the value of the input field (text) in its state and updates it through the `onChange` event handler. This ensures that the input's value is always synchronized with the component's state, making it a controlled component. Controlled components are useful for managing and validating user input in React applications.
|
||||
|
||||
```javascript
|
||||
import React , {useState} from 'react'
|
||||
|
||||
const Controlled = () => {
|
||||
|
||||
const [text , setText] = useState('')
|
||||
|
||||
const handleSubmit = (e)=>{
|
||||
e.preventDefault()
|
||||
|
||||
console.log(text)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<form>
|
||||
<input type='text' value={text} placeholder='controlled' onChange={(e)=>setText(e.target.value)}/>
|
||||
<button onClick={handleSubmit}>Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Controlled
|
||||
```
|
||||
|
||||
**Uncontrolled Components:** Uncontrolled components are React components where the value of an input element is not controlled by React state but is directly managed by the DOM. In the given below example, the `Uncontrolled` component uses a `ref` (`inputRef`) to directly access the input field's value from the DOM when needed. Unlike controlled components, React doesn't track or manage the input value's changes in its state. Uncontrolled components are useful in cases where you need to integrate with non-React code or libraries and need direct access to DOM elements.
|
||||
|
||||
```javascript
|
||||
import React , {useRef} from 'react'
|
||||
|
||||
const Uncontrolled = () => {
|
||||
const inputRef = useRef(null)
|
||||
|
||||
|
||||
const handleSubmit = (e)=>{
|
||||
e.preventDefault()
|
||||
console.log(inputRef.current.value)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<form>
|
||||
<input type='text' placeholder='uncontrolled' ref={inputRef}/>
|
||||
<button onClick={handleSubmit}>Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Uncontrolled
|
||||
```
|
||||
|
@@ -0,0 +1,732 @@
|
||||
|
||||
|
||||
# Component Architecture
|
||||
- Component is a section of an architecture.
|
||||
- When we divide the complete projects into small sections then that architecture is known as component architecture.
|
||||
- Every section of an architecture is a separate section in itself.
|
||||
|
||||
**Example:**
|
||||
Let us take an example of LinkedIn, in the LinkedIn search box, news, connections, etc. all are different sections, which are built separately but they work together.
|
||||
|
||||
## Creating Components
|
||||
- Everything written in the app.js is rendered on the website.
|
||||
|
||||
Here let us take an example of a demo component before creating our component:
|
||||
```javascript=
|
||||
function App(){
|
||||
return {
|
||||
<div>
|
||||
<h1> Hello from react</h1>
|
||||
</div>
|
||||
};
|
||||
}
|
||||
export default App
|
||||
```
|
||||
|
||||
- Components that are created with function are known as functional components.
|
||||
- Here is a function with the name `App` and this function returns a `div` which contains the `h1` tag.
|
||||
|
||||
|
||||
|
||||
### Functional Component
|
||||
You can define a Javascript function and from that function, you can return JSX code and that function will be able to accept props.
|
||||
|
||||
**Props** is an object that can have different values for every component.
|
||||
|
||||
**JSX:** is a way of writing Javascript and HTML together.
|
||||
|
||||
|
||||
**Creating First Component:**
|
||||
1. Inside `src`, we will create a folder named `components`.
|
||||
2. Let us create the first component inside this folder with the name `MyComponent.js`.
|
||||
3. **Component names always start with a capital letter.**
|
||||
4. Now open `MyComponent.js`, now we will create a functional component here with the name `MyComponent`.
|
||||
5. We will create a function with the name `MyComponent`.
|
||||
|
||||
```javascript=
|
||||
function MyComponent(){
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
6. This function simply returns a `div` with a `h1`.
|
||||
|
||||
|
||||
```javascript=
|
||||
function MyComponent(){
|
||||
return{
|
||||
<div>
|
||||
<h1> My first component </h1>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
7. Now we need to export this component using the `export` keyword.
|
||||
|
||||
```javascript=
|
||||
function MyComponent(){
|
||||
return{
|
||||
<div>
|
||||
<h1> My first component </h1>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default MyComponent
|
||||
```
|
||||
|
||||
Here `default` ensures that this component can be used anywhere inside the app.
|
||||
|
||||
8. Now we need to import the component inside the main component i.e. `app.js`.
|
||||
9. We need to write the below line inside `app.js` to import `MyComponent`.
|
||||
|
||||
```javascript=
|
||||
import MyComponent from './components/MyComponent';
|
||||
function App(){
|
||||
return {
|
||||
<div>
|
||||
<h1> Hello from react</h1>
|
||||
</div>
|
||||
};
|
||||
}
|
||||
export default App
|
||||
```
|
||||
|
||||
10. Now we will use `MyComponent` inside the `App` component of the `App.js` file.
|
||||
|
||||
```javascript=
|
||||
import MyComponent from './components/MyComponent';
|
||||
function App(){
|
||||
return {
|
||||
<div>
|
||||
<h1> Hello from react</h1>
|
||||
<MyComponent/>
|
||||
</div>
|
||||
};
|
||||
}
|
||||
export default App
|
||||
```
|
||||
|
||||
|
||||
**Creating Second Component**
|
||||
|
||||
1. Create a file name `MyComponent2.js` inside the `components` folder.
|
||||
2. Open the `MyComponent2.js` file.
|
||||
3. Now type `rfce` to create a functional component automatically.
|
||||
4. Here `rfce` stands for react functional component export.
|
||||
5. After typing `rfce` press enter, and then a functional component is created automatically.
|
||||
6. And below code will appear automatically.
|
||||
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
function MyComponent2(){
|
||||
return{
|
||||
<div>MyComponent2</div>
|
||||
}
|
||||
}
|
||||
export default MyComponent2
|
||||
```
|
||||
|
||||
7. Now we just have to write our content inside this component.
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
function MyComponent2(){
|
||||
return{
|
||||
<div>
|
||||
<h3>this is my component2</h3>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default MyComponent2
|
||||
```
|
||||
|
||||
|
||||
8. Now we can import MyComponent2 in `app.js` by writing the below line inside `app.js` to import `MyComponent2`.
|
||||
|
||||
```javascript=
|
||||
import MyComponent2 from './components/MyComponent2';
|
||||
import MyComponent from './components/MyComponent';
|
||||
function App(){
|
||||
return {
|
||||
<div>
|
||||
<h1> Hello from react</h1>
|
||||
<MyComponent/>
|
||||
</div>
|
||||
};
|
||||
}
|
||||
export default App
|
||||
```
|
||||
|
||||
9. Now we will use `MyComponent2` inside the `App` component of the `App.js` file.
|
||||
|
||||
```javascript=
|
||||
import MyComponent2 from './components/MyComponent2';
|
||||
import MyComponent from './components/MyComponent';
|
||||
function App(){
|
||||
return {
|
||||
<div>
|
||||
<h1> Hello from react</h1>
|
||||
<MyComponent/>
|
||||
<MyComponent2/>
|
||||
</div>
|
||||
};
|
||||
}
|
||||
export default Appexport default App
|
||||
```
|
||||
|
||||
10. We can see that we can use multiple components in a single app component.
|
||||
|
||||
|
||||
|
||||
|
||||
# JSX
|
||||
- JSX is not HTML. We can use also javascript inside it.
|
||||
|
||||
**Always write javascript code above the return statement.**
|
||||
|
||||
Let us take an example of the `MyComponent.js` file.
|
||||
|
||||
1. Open `MyComponent.js` file.
|
||||
2. Declare one javascript variable inside it.
|
||||
|
||||
```javascript=
|
||||
function MyComponent(){
|
||||
const name = 'Steve'
|
||||
return{
|
||||
<div>
|
||||
<h1> My first component </h1>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default MyComponent
|
||||
```
|
||||
|
||||
3. We can use this javascript variable inside the return statement.
|
||||
|
||||
|
||||
```javascript=
|
||||
function MyComponent(){
|
||||
const name = 'Steve'
|
||||
return{
|
||||
<div>
|
||||
<h1> My name is {name} </h1>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default MyComponent
|
||||
```
|
||||
|
||||
4. Now the value of the name variable i.e. `Steve` will be displayed in output.
|
||||
5. This is only possible due to **JSX**, which allows us to combine JavaScript with HTML, as it is not possible with HTML only.
|
||||
6. As we defined variable in javascript, we can also define a function, which can be called.\
|
||||
|
||||
|
||||
```javascript=
|
||||
function MyComponent(){
|
||||
const name = 'Steve'
|
||||
const showMessage =()=>{
|
||||
return 'hello this is a message from showMessage function'
|
||||
}
|
||||
return{
|
||||
<div>
|
||||
<h1> My name is {name} {showMessage()} </h1>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default MyComponent
|
||||
```
|
||||
|
||||
7. Now the value returned by the function is always displayed in output.
|
||||
|
||||
|
||||
|
||||
|
||||
# Component Reusability
|
||||
- Components can be reused in react.
|
||||
- And according to our use case we can pass and modify data by something called **props** and **state**.
|
||||
|
||||
|
||||
**Reusing a component:**
|
||||
We can use a single component multiple times.
|
||||
|
||||
```javascript=
|
||||
import MyComponent2 from './components/MyComponent2';
|
||||
import MyComponent from './components/MyComponent';
|
||||
function App(){
|
||||
return {
|
||||
<div>
|
||||
<h1> Hello from react</h1>
|
||||
<MyComponent/>
|
||||
<MyComponent/>
|
||||
<MyComponent/>
|
||||
<MyComponent/>
|
||||
<MyComponent2/>
|
||||
</div>
|
||||
};
|
||||
}
|
||||
export default App
|
||||
```
|
||||
|
||||
As in this, all the components have the same data, we can use the same component again and again but we can also pass different data to component by **props.**
|
||||
|
||||
## Props
|
||||
- Props stands for properties.
|
||||
- And every component can have different properties.
|
||||
|
||||
1. Let us create a new component `UserProfile`, and create a file `UserProfile.js` inside the components folder.
|
||||
2. Now inside `UserProfile.js` write `rfce` and press enter and then write our content.
|
||||
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
function UserProfile(){
|
||||
return{
|
||||
<div>
|
||||
<h3>Name: Steve, Age: 25, Occupation: Software Engineer</h3>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default UserProfile
|
||||
```
|
||||
|
||||
3. Now import this component in `app.js` file.
|
||||
|
||||
|
||||
```javascript=
|
||||
import UserProfile from './components/UserProfile';
|
||||
|
||||
function App(){
|
||||
return {
|
||||
<div>
|
||||
<UserProfile/>
|
||||
</div>
|
||||
};
|
||||
}
|
||||
export default App
|
||||
```
|
||||
|
||||
4. Now we want to reuse the `UserProfile` component.
|
||||
5. Now we will use props.
|
||||
6. And we will access props using the key inside it.
|
||||
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
function UserProfile(props){
|
||||
return{
|
||||
<div>
|
||||
<h3>Name: {props.name}, Age: {props.age}, Occupation: {props.occupation}</h3>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default UserProfile
|
||||
```
|
||||
|
||||
7. And then we will pass different values for calling the component.
|
||||
|
||||
|
||||
```javascript=
|
||||
import UserProfile from './components/UserProfile';
|
||||
|
||||
function App(){
|
||||
return {
|
||||
<div>
|
||||
<UserProfile name='Steve' age={25} occupation='Software Engineer'/>
|
||||
<UserProfile name='John' age={35} occupation='Civil Engineer'/>
|
||||
<UserProfile name='Mark' age={40} occupation='Businessmen'/>
|
||||
</div>
|
||||
};
|
||||
}
|
||||
export default App
|
||||
```
|
||||
|
||||
**Integer value always passed in {}**
|
||||
Now we can see that we are using the same component three times with different data. So our output will be something like this.
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
# Disadvantages of Props
|
||||
**Props are immutable**, which means we can not change it at the component level.
|
||||
Whatever value is coming from the main component, we need to use only that value, we can not change it inside the component.
|
||||
|
||||
|
||||
|
||||
# State
|
||||
- There are two types of components in react, one is a class-based component and another is a functional component.
|
||||
- Functional component is also known as state-less component, but **hooks** are introduced that allow functional components to have access to state and other React features.
|
||||
|
||||
|
||||
|
||||
# Hooks
|
||||
Hooks are introduced that allow functional components to have access to state and other React features.
|
||||
The first hook is `useState`.
|
||||
|
||||
|
||||
|
||||
|
||||
# Event handling
|
||||
We just want to create a button, we want that when that button is clicked then a message is displayed on the console.
|
||||
|
||||
1. Let us create a new component named `Event.js`, so create a file named `Event.js` inside the components folder.
|
||||
2. Inside this component we will create a button.
|
||||
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
function Event(){
|
||||
return{
|
||||
<div>
|
||||
<h1> Event handling</h1>
|
||||
<button>click me</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Event
|
||||
```
|
||||
|
||||
2. We need to import this component inside the `App` component.
|
||||
|
||||
```javascript=
|
||||
import Event from './components/Event';
|
||||
|
||||
function App(){
|
||||
return {
|
||||
<div>
|
||||
<Event/>
|
||||
</div>
|
||||
};
|
||||
}
|
||||
export default App
|
||||
```
|
||||
|
||||
3. Now for performing event handling(when the button is clicked then a message is displayed on the console), we will create a function inside the `Event` component.
|
||||
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
function Event(){
|
||||
const handleClick=()=>{
|
||||
console.log('Button was clicked')
|
||||
}
|
||||
return{
|
||||
<div>
|
||||
<h1> Event handling</h1>
|
||||
<button>click me</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Event
|
||||
```
|
||||
|
||||
4. Now we need to call this function on the `onClick` event of the button.
|
||||
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
function Event(){
|
||||
const handleClick=()=>{
|
||||
console.log('Button was clicked')
|
||||
}
|
||||
return{
|
||||
<div>
|
||||
<h1> Event handling</h1>
|
||||
<button onClick={handleClick}>click me</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Event
|
||||
```
|
||||
|
||||
5. Now when we click on this a message is displayed on the console.
|
||||
|
||||
|
||||
|
||||
|
||||
# Hooks
|
||||
- **Functional component are stateless component**, means it does not has any state,
|
||||
- **Class Based components are stateful components.**
|
||||
|
||||
|
||||
**Hooks are there to implement all the functionality of the state in the functional component.**
|
||||
|
||||
First, we will learn the `useState` hook.
|
||||
|
||||
|
||||
|
||||
# useState
|
||||
We will create a simple application, which has two buttons one button will increment the value and another button will decrement the value.
|
||||
|
||||
1. We will create a component named `Counter`, so create a file named `Counter.js` inside the components folder.
|
||||
2. Inside `Counter.js`, write `rfce` and press enter.
|
||||
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
function Counter(){
|
||||
return{
|
||||
<div>
|
||||
<h1> Counter</h1>
|
||||
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Counter
|
||||
```
|
||||
|
||||
3. Import the `Counter` component inside the `App` component.
|
||||
|
||||
|
||||
```javascript=
|
||||
import Counter from './components/Counter';
|
||||
|
||||
function App(){
|
||||
return {
|
||||
<div>
|
||||
<Counter/>
|
||||
</div>
|
||||
};
|
||||
}
|
||||
export default App
|
||||
```
|
||||
|
||||
4. Now we will create two buttons inside the `Counter` component one for increment and another one for decrement, and we will also display the count in the `Counter` component.
|
||||
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
function Counter(){
|
||||
return{
|
||||
<div>
|
||||
<h2>The count value is</h2>
|
||||
<button>Increment</button>
|
||||
<button>Decrement</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Counter
|
||||
```
|
||||
|
||||
5. **useState hook is a function that is inbuilt react, and it will give two values one value is variable and another value is function.**
|
||||
6. First we need to import `useState`.
|
||||
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
import {useState} from 'react'
|
||||
function Counter(){
|
||||
return{
|
||||
<div>
|
||||
<h2>The count value is</h2>
|
||||
<button>Increment</button>
|
||||
<button>Decrement</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Counter
|
||||
```
|
||||
|
||||
|
||||
## Syntax of useState
|
||||
```javascript=
|
||||
const[variable, function]=useState()
|
||||
```
|
||||
|
||||
Whatever value is passed inside the useState that will be used for initialization of the variable, like in the below example variable will be initialized with 0.
|
||||
|
||||
|
||||
```javascript=
|
||||
const[variable, function]=useState(0)
|
||||
```
|
||||
|
||||
7. Inside our functional component, we will use `useState`, and initialize count by 0.
|
||||
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
import {useState} from 'react'
|
||||
function Counter(){
|
||||
const[count, setCount] = useState(0)
|
||||
|
||||
return{
|
||||
<div>
|
||||
<h2>The count value is </h2>
|
||||
<button>Increment</button>
|
||||
<button>Decrement</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Counter
|
||||
```
|
||||
|
||||
8. We will create increment and decrement functions, and call those functions on the `onClick` event of buttons.
|
||||
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
import {useState} from 'react'
|
||||
function Counter(){
|
||||
const[count, setCount] = useState(0)
|
||||
function increment(){
|
||||
setCount(count+1)
|
||||
}
|
||||
function decrement(){
|
||||
setCount(count-1)
|
||||
}
|
||||
return{
|
||||
<div>
|
||||
<h2>The count value is </h2>
|
||||
<button onClick={increment}>Increment</button>
|
||||
<button onClick={decrement}>Decrement</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Counter
|
||||
```
|
||||
|
||||
9. We will display this `count` value inside h2.
|
||||
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
import {useState} from 'react'
|
||||
function Counter(){
|
||||
const[count, setCount] = useState(0)
|
||||
function increment(){
|
||||
setCount(count+1)
|
||||
}
|
||||
function decrement(){
|
||||
setCount(count-1)
|
||||
}
|
||||
return{
|
||||
<div>
|
||||
<h2>The count value is {count}</h2>
|
||||
<button onClick={increment}>Increment</button>
|
||||
<button onClick={decrement}>Decrement</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Counter
|
||||
```
|
||||
|
||||
Now if we see the output our count value is initially displayed as 0, and when we click the increment button, the value will increment, and then the incremented value will be displayed, and when we click the decrement button, then the value will decrement, and then the decremented value will be displayed,
|
||||
|
||||
10. We can also set the condition that the count will not be decremented only if its value is 0.
|
||||
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
import {useState} from 'react'
|
||||
function Counter(){
|
||||
const[count, setCount] = useState(0)
|
||||
function increment(){
|
||||
setCount(count+1)
|
||||
}
|
||||
function decrement(){
|
||||
if(count===0){
|
||||
setCount(count)
|
||||
}
|
||||
else{
|
||||
setCount(count-1)
|
||||
}
|
||||
}
|
||||
return{
|
||||
<div>
|
||||
<h2>The count value is {count}</h2>
|
||||
<button onClick={increment}>Increment</button>
|
||||
<button onClick={decrement}>Decrement</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Counter
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
# Destructuring
|
||||
- destructuring values inside the props.
|
||||
- Props is an object, that has a key-value pair.
|
||||
- Now we will do destructuring inside the `UserProfile` component.
|
||||
|
||||
|
||||
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
function UserProfile(props){
|
||||
// destructuring
|
||||
const {name, age, occupation} = props
|
||||
return{
|
||||
<div>
|
||||
<h3>Name: {props.name}, Age: {props.age}, Occupation: {props.occupation}</h3>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default UserProfile
|
||||
```
|
||||
|
||||
- Now we can access values directly by the variable.
|
||||
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
function UserProfile(props){
|
||||
// destructuring
|
||||
const {name, age, occupation} = props
|
||||
return{
|
||||
<div>
|
||||
<h3>Name: {name}, Age: {age}, Occupation: {occupation}</h3>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default UserProfile
|
||||
```
|
||||
|
||||
|
||||
- We can also do destructuring in this way.
|
||||
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
function UserProfile({name, age, occupation}){
|
||||
return{
|
||||
<div>
|
||||
<h3>Name: {name}, Age: {age}, Occupation: {occupation}</h3>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default UserProfile
|
||||
```
|
||||
|
||||
|
||||
# DOM
|
||||
|
||||
- DOM is a tree.
|
||||
|
||||

|
||||
|
||||
- **DOM Manipulations/Operations:** In these elements we can perform some operations, like remove them, add them, insert some data inside them, etc.
|
||||
|
||||
- When the DOM manipulation happens, then the whole DOM gets rerendered.
|
||||
|
||||
- **Re-Rendering:** When we change anything in a single element, then the whole tree is re-rendered.
|
||||
- And re-rendering of the whole DOM is not so good, it makes the application slow.
|
||||
- **To overcome this problem, react introduced virtual DOM.**
|
||||
|
||||
|
||||
# Virtual DOM
|
||||
|
||||
Virtual DOM is in memory copy of actual DOM.
|
||||
- Let us assume we have a real DOM.
|
||||
|
||||

|
||||
|
||||
|
||||
- Now react will create a copy of this DOM.
|
||||
|
||||

|
||||
|
||||
- Now whenever any change happens in any particular element, then react will first make changes in the virtual DOM, it will not do changes directly in the real DOM.
|
||||
- Let us assume a `h2` element is changed, then react will do these changes in virtual DOM.
|
||||
|
||||

|
||||
|
||||
- Now again any other changes happen in any element, let us suppose the `p` element is changed.
|
||||
- So React will make changes in another virtual DOM.
|
||||
|
||||

|
||||
|
||||
- **React will keep a record of all the changes and keep creating a virtual DOM at every change.**
|
||||
- And last virtual DOM has all the changes.
|
||||
- So according to the final/last virtual DOM, it will re-render the real DOM only once. This process that virtual DOM follows is known as **batch processing.**
|
||||
|
||||
And whole process of creating a virtual DOM at every updation, keeping a copy of every virtual DOM and re-rendering the real DOM once is known as **Reconcillation**.
|
||||
And algorithm used by virtual DOM to compare all the virtual DOMs is known as **diffing algo**
|
@@ -0,0 +1,783 @@
|
||||
|
||||
|
||||
# Lists in React
|
||||
|
||||
Let us suppose we have a list of products,
|
||||
`productArray = [laptop, headphones, mouse]`
|
||||
|
||||
1. Let us create a component named `Products`, so we will create a file named `Products.js` inside the components folder.
|
||||
2. Now open `Products.js`, then type `rfce` and press enter.
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
function Products(){
|
||||
return{
|
||||
<div>Products</div>
|
||||
}
|
||||
}
|
||||
export default Products
|
||||
```
|
||||
|
||||
3. Let us assume we have a `products` array, which contains products.
|
||||
```javascript
|
||||
import React from 'react'
|
||||
function Products(){
|
||||
let products = ['Laptop', 'Headphones', 'Keyboard', 'Mouse']
|
||||
return{
|
||||
<div>Products</div>
|
||||
}
|
||||
}
|
||||
export default Products
|
||||
```
|
||||
|
||||
4. Now we want to show all the elements of the `products` array in the HTML.
|
||||
5. The first way is to simply iterate and display every element.
|
||||
```javascript
|
||||
import React from 'react'
|
||||
function Products(){
|
||||
let products = ['Laptop', 'Headphones', 'Keyboard', 'Mouse']
|
||||
return{
|
||||
<div>
|
||||
for(let i = 0;i < products.length; i++){
|
||||
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Products
|
||||
```
|
||||
|
||||
Here syntax is correct, but it will give an error, as JSX does not allow us to write a loop in this way. In place of loops, we can higher-order functions.
|
||||
|
||||
6. We will use `map` with an array, by which we get access to all the array elements one by one. We will create a list element of every element one by one.
|
||||
```javascript
|
||||
import React from 'react'
|
||||
function Products(){
|
||||
let products = ['Laptop', 'Headphones', 'Keyboard', 'Mouse']
|
||||
return{
|
||||
<div>
|
||||
{products.map((product) => {
|
||||
return <li>{product}</li>
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Products
|
||||
```
|
||||
|
||||
7. Now we need to import the `Products` component in `App`.
|
||||
|
||||
|
||||
```javascript=
|
||||
import Products from './components/Products';
|
||||
function App(){
|
||||
return {
|
||||
<div>
|
||||
<Products/>
|
||||
</div>
|
||||
};
|
||||
}
|
||||
export default App
|
||||
```
|
||||
|
||||
When we run this, and we will see the output we will get the correct output, but a warning is also there in the console.
|
||||
|
||||

|
||||
|
||||
This warning says that every child in a list should have a unique 'key' prop.
|
||||
|
||||
- React says whenever you create a list element, you should provide a unique key to it so that when React updates the DOM, it can uniquely identify what is required to be updated.
|
||||
The key is a unique ID.
|
||||
|
||||
So we will create a key for every list element, and we are creating the key with the product name, as every product has a different name.
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
function Products(){
|
||||
let products = ['Laptop', 'Headphones', 'Keyboard', 'Mouse']
|
||||
return{
|
||||
<div>
|
||||
{products.map((product) => {
|
||||
return <li key = {product}>{product}</li>
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Products
|
||||
```
|
||||
|
||||
- But in case, we have two products with the same name, `let products = ['Laptop', 'Headphones', 'Keyboard', 'Mouse', 'Laptop']`
|
||||
```javascript
|
||||
import React from 'react'
|
||||
function Products(){
|
||||
let products = ['Laptop', 'Headphones', 'Keyboard', 'Mouse', 'Laptop']
|
||||
return{
|
||||
<div>
|
||||
{products.map((product) => {
|
||||
return <li key = {product}>{product}</li>
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Products
|
||||
```
|
||||
|
||||
- Now the problem will occur, as the two products have the same name, so the key will not remain unique as the same laptop key is assigned to two products.
|
||||
- Now we will again get a warning `encountered two children with a same key laptop`.
|
||||
- So we can use index, `map()` will also have access to all the indexes. And every array element has a unique index, starting from 0, 1, 2, 3, .....
|
||||
- So we can make the key using the index.
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
function Products(){
|
||||
let products = ['Laptop', 'Headphones', 'Keyboard', 'Mouse', 'Laptop']
|
||||
return{
|
||||
<div>
|
||||
{products.map((product, index) => {
|
||||
return <li key = {index}>{product}</li>
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Products
|
||||
```
|
||||
|
||||
## Using objects
|
||||
|
||||
1. We will create an array of objects for products.
|
||||
|
||||
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
function Products(){
|
||||
let productsList = [
|
||||
{ id: 1, name: "Laptop", price: 35000},
|
||||
{ id: 2, name: "Headphones", price: 12000},
|
||||
{ id: 3, name: "Mouse", price: 8000},
|
||||
{ id: 4, name: "Keyboard", price: 10000},
|
||||
]
|
||||
return{
|
||||
<div>
|
||||
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Products
|
||||
```
|
||||
|
||||
2. Now we will display this product list in HTML using `map`. Now we are using the `id` of every object of an array for creating the key and then we are displaying the name and price of that particular product.
|
||||
|
||||
|
||||
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
function Products(){
|
||||
let productsList = [
|
||||
{ id: 1, name: "Laptop", price: 35000},
|
||||
{ id: 2, name: "Headphones", price: 12000},
|
||||
{ id: 3, name: "Mouse", price: 8000},
|
||||
{ id: 4, name: "Keyboard", price: 10000},
|
||||
]
|
||||
return{
|
||||
<div>
|
||||
{
|
||||
productsList.map((product) => {
|
||||
return <li key = {product.id}>{product.name} : {product.price}</li>
|
||||
})
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Products
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||

|
||||
|
||||
|
||||
- We can also remove the return keyword written before `<li>`, but then we need to enclose the list element creation code in `()`, instead of `{}`.
|
||||
|
||||
|
||||
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
function Products(){
|
||||
let productsList = [
|
||||
{ id: 1, name: "Laptop", price: 35000},
|
||||
{ id: 2, name: "Headphones", price: 12000},
|
||||
{ id: 3, name: "Mouse", price: 8000},
|
||||
{ id: 4, name: "Keyboard", price: 10000},
|
||||
]
|
||||
return{
|
||||
<div>
|
||||
{
|
||||
productsList.map((product) => (
|
||||
<li key = {product.id}>{product.name} : {product.price}</li>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Products
|
||||
```
|
||||
|
||||
|
||||
|
||||
# Form Handling in React
|
||||
- **Form** is a layout which will accept data from the user.
|
||||
- Form has text boxes, labels, checkboxes, buttons, etc. by which the user can provide some input.
|
||||
- Then the user will click on submit, then all the data will go to the database, or be sent for some processing.
|
||||
|
||||
|
||||
**In react, we can also work with forms**.
|
||||
|
||||
|
||||
1. Let us create a component named `Form`, so first we will create a file named `Form.js` inside the components folder.
|
||||
2. Now open `Form.js`, then type `rfce` and press enter.
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
function Form(){
|
||||
return{
|
||||
<div>Form</div>
|
||||
}
|
||||
}
|
||||
export default Form
|
||||
```
|
||||
|
||||
3. Let us create a form by the `<form>` tag, and inside that form, we will create one label and input field.
|
||||
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
function Form(){
|
||||
return{
|
||||
<div>
|
||||
<h1>This is a form</h1>
|
||||
<form>
|
||||
<label>firstName</label>
|
||||
<input type = "text" value = "firstName" ></input>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Form
|
||||
```
|
||||
|
||||
|
||||
4. Now we need to import the `Form` component in `App`.
|
||||
|
||||
|
||||
```javascript
|
||||
import Form from './components/Form';
|
||||
function App(){
|
||||
return {
|
||||
<div>
|
||||
<Form/>
|
||||
</div>
|
||||
};
|
||||
}
|
||||
export default App
|
||||
```
|
||||
|
||||
- But now if we try to change the data of the input field we are not able to change it, but in HTML if we create an input field we can change the data of it.
|
||||
- This is because props are immutable. And `value` of the `input` field is behaving like props, and that's why we are not able to update it.
|
||||
- And if we want to change the value of prop, then we need to use state.
|
||||
- But in the functional components we don't use state, so in place of it **we will use the useState hook**.
|
||||
|
||||
1. Firstly we need to import the `useState` hook.
|
||||
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
import {useState} from 'react'
|
||||
function Form(){
|
||||
return{
|
||||
<div>
|
||||
<h1>This is a form</h1>
|
||||
<form>
|
||||
<label>firstName</label>
|
||||
<input type = "text" value = "firstName" ></input>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Form
|
||||
```
|
||||
|
||||
2. Now we will use the `useState()` hook.
|
||||
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
import {useState} from 'react'
|
||||
function Form(){
|
||||
const [firstName, setFirstName] = useState('')
|
||||
return{
|
||||
<div>
|
||||
<h1>This is a form</h1>
|
||||
<form>
|
||||
<label>firstName</label>
|
||||
<input type = "text" value = {firstName} ></input>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Form
|
||||
```
|
||||
|
||||
3. We have initialized `firstName` with an empty string, but for updating the value of `firstName` we will create a function `handleChange`.
|
||||
Whenever there is a change in the value of the input field this function will be called so we will call this function on the `onChange` event of the input field and this function will receive an event as `e`.
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
import {useState} from 'react'
|
||||
function Form(){
|
||||
const [firstName, setFirstName] = useState('')
|
||||
let handleChange = (e) => {
|
||||
|
||||
}
|
||||
return{
|
||||
<div>
|
||||
<h1>This is a form</h1>
|
||||
<form>
|
||||
<label>firstName</label>
|
||||
<input type = "text" onChange = {handleChange} value = {firstName} ></input>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Form
|
||||
```
|
||||
|
||||
4. Inside this function we will call the `setFirstName` with the new typed value so that the `firstName` will be updated to a new typed value.
|
||||
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
import {useState} from 'react'
|
||||
function Form(){
|
||||
const [firstName, setFirstName] = useState('')
|
||||
let handleChange = (e) => {
|
||||
setFirstName(e.target.value)
|
||||
}
|
||||
return{
|
||||
<div>
|
||||
<h1>This is a form</h1>
|
||||
<form>
|
||||
<label>firstName</label>
|
||||
<input type = "text" onChange = {handleChange} value = {firstName} ></input>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Form
|
||||
```
|
||||
|
||||
**Submitting form data**
|
||||
|
||||
1. Creating a submit button.
|
||||
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
import {useState} from 'react'
|
||||
function Form(){
|
||||
const [firstName, setFirstName] = useState('')
|
||||
let handleChange = (e) => {
|
||||
setFirstName(e.target.value)
|
||||
}
|
||||
return{
|
||||
<div>
|
||||
<h1>This is a form</h1>
|
||||
<form>
|
||||
<label>firstName</label>
|
||||
<input type = "text" onChange = {handleChange} value = {firstName} ></input>
|
||||
<button>Submit Form Button</button>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Form
|
||||
```
|
||||
|
||||
2. We will create a function `handleSubmit` and call it on the `onSubmit` event of the form. And `handleSubmit` will also receive the event as a parameter. Inside that function, we will use `e.preventDefault()` to avoid automatic refreshing of the page and we will print `firstName` inside this function.
|
||||
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
import {useState} from 'react'
|
||||
function Form(){
|
||||
const [firstName, setFirstName] = useState('')
|
||||
let handleChange = (e) => {
|
||||
setFirstName(e.target.value)
|
||||
}
|
||||
let handleSubmit = (e) => {
|
||||
e.preventDefault()
|
||||
console.log(firstName)
|
||||
}
|
||||
return{
|
||||
<div>
|
||||
<h1>This is a form</h1>
|
||||
<form onSubmit = {handleSubmit}>
|
||||
<label>firstName</label>
|
||||
<input type = "text" onChange = {handleChange} value = {firstName} ></input>
|
||||
<button>Submit Form Button</button>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Form
|
||||
```
|
||||
|
||||
|
||||
# Form with firstName and lastName
|
||||
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
import {useState} from 'react'
|
||||
function Form(){
|
||||
const [firstName, setFirstName] = useState('')
|
||||
const [lastName, setLastName] = useState('')
|
||||
let handleFirstNameChange = (e) => {
|
||||
setFirstName(e.target.value)
|
||||
}
|
||||
let handleLastNameChange = (e) => {
|
||||
setLastName(e.target.value)
|
||||
}
|
||||
let handleSubmit = (e) => {
|
||||
e.preventDefault()
|
||||
console.log(firstName, lastName)
|
||||
}
|
||||
return{
|
||||
<div>
|
||||
<h1>This is a form</h1>
|
||||
<form onSubmit = {handleSubmit}>
|
||||
<label>firstName</label>
|
||||
<input type = "text" onChange = {handleFirstNameChange} value = {firstName} ></input>
|
||||
<label>lastName</label>
|
||||
<input type = "text" onChange = {handleLastNameChange} value = {lastName} ></input>
|
||||
<button>Submit Form Button</button>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Form
|
||||
```
|
||||
|
||||
|
||||
We can also print data on the console in object form.
|
||||
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
import {useState} from 'react'
|
||||
function Form(){
|
||||
const [firstName, setFirstName] = useState('')
|
||||
const [lastName, setLastName] = useState('')
|
||||
let handleFirstNameChange = (e) => {
|
||||
setFirstName(e.target.value)
|
||||
}
|
||||
let handleLastNameChange = (e) => {
|
||||
setLastName(e.target.value)
|
||||
}
|
||||
let handleSubmit = (e) => {
|
||||
e.preventDefault()
|
||||
console.log(
|
||||
{
|
||||
fName : firstName,
|
||||
lName : lastName
|
||||
})
|
||||
}
|
||||
return{
|
||||
<div>
|
||||
<h1>This is a form</h1>
|
||||
<form onSubmit = {handleSubmit}>
|
||||
<label>firstName</label>
|
||||
<input type = "text" onChange = {handleFirstNameChange} value = {firstName} ></input>
|
||||
<label>lastName</label>
|
||||
<input type = "text" onChange = {handleLastNameChange} value = {lastName} ></input>
|
||||
<button>Submit Form Button</button>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Form
|
||||
```
|
||||
|
||||
|
||||
# useEffect hook
|
||||
When there are only class-based components in react, then there are life cycle methods.
|
||||
**Life Cycle Methods of a Component**
|
||||
1. Component Did Mount
|
||||
2. Component Did Update
|
||||
3. Component Did Unmount.
|
||||
|
||||
**Component Did Mount:** When react renders the component for the first time.
|
||||
**Component Did Update:** When you do some updation in the component.
|
||||
**Component Did Unmount:** Removing component from the life cycle.
|
||||
|
||||
All these life cycle methods are used by class-based components.
|
||||
Functional Components handles all the processes using a single hook i.e. `useEffect` hook.
|
||||
|
||||
1. Let us create a component named `Ue1`, so first we will create a file named `Ue1.js` inside the components folder.
|
||||
2. Now open `Ue1.js`, then type `rfce` and press enter.
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
function Ue1(){
|
||||
return{
|
||||
<div>Ue1</div>
|
||||
}
|
||||
}
|
||||
export default Ue1
|
||||
```
|
||||
|
||||
|
||||
3. Now we need to import the `Ue1` component in `App`.
|
||||
|
||||
|
||||
```javascript
|
||||
import Ue1 from './components/Ue1';
|
||||
function App(){
|
||||
return {
|
||||
<div>
|
||||
<Ue1/>
|
||||
</div>
|
||||
};
|
||||
}
|
||||
export default App
|
||||
```
|
||||
|
||||
4. In `Ue1` we will simply create a counter. We will display the count value and create a button for incrementing the count value.
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
function Ue1(){
|
||||
return{
|
||||
<div>
|
||||
<h1>This is my Count value :</h1>
|
||||
<button>increment</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Ue1
|
||||
```
|
||||
|
||||
5. We will use `useState` to update the value of the count on click on the increment button.
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
import {useState} from 'react'
|
||||
function Ue1(){
|
||||
const[count, setCount] = useState(0)
|
||||
let incrementCount = () => {
|
||||
setCount(count + 1)
|
||||
}
|
||||
return{
|
||||
<div>
|
||||
<h1>This is my Count value : {count}</h1>
|
||||
<button onClick = {incrementCount}>increment</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Ue1
|
||||
```
|
||||
|
||||
## Syntax of useEffect
|
||||
|
||||
```javascript
|
||||
useEffect(() => {
|
||||
// operations
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
## ComponentMounting and ComponentUpdation
|
||||
|
||||
1. Before using `useEffect`, we need to import it. After that, we will `useEffect`.
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
import {useState, useEffect} from 'react'
|
||||
function Ue1(){
|
||||
const[count, setCount] = useState(0)
|
||||
let incrementCount = () => {
|
||||
setCount(count + 1)
|
||||
}
|
||||
useEffect(()=>{
|
||||
console.log('use Effect Runs')
|
||||
document.title = `Button clicked for ${count} times`
|
||||
})
|
||||
console.log('Other code that gets executed')
|
||||
return{
|
||||
<div>
|
||||
<h1>This is my Count value : {count}</h1>
|
||||
<button onClick = {incrementCount}>increment</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Ue1
|
||||
```
|
||||
|
||||
But when we see the output of it the title is updated after some delay in the updation of the heading.
|
||||
|
||||
This is because `useEffect` is by default asynchronous hook, so it will run at the last.
|
||||
|
||||
- If we check our console, then first `Other code that gets executed` will be printed then `use Effect Runs` will be printed.
|
||||
- The `useEffect` code runs at the end, firstly all other code will be executed.
|
||||
This is because it takes care of two things at a time i.e. `Component Did Mount` and `Component Did Update`.
|
||||
|
||||
- When there is any updation in an application, then `useEffect` will take care of it.
|
||||
|
||||
|
||||
## ComponentMounting only
|
||||
- If we add an empty dependency array in 'useEffect`.
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
import {useState, useEffect} from 'react'
|
||||
function Ue1(){
|
||||
const[count, setCount] = useState(0)
|
||||
let incrementCount = () => {
|
||||
setCount(count + 1)
|
||||
}
|
||||
useEffect(() => {
|
||||
console.log('use Effect Runs')
|
||||
document.title = `Button clicked for ${count} times`
|
||||
}, [])
|
||||
console.log('Other code that gets executed')
|
||||
return{
|
||||
<div>
|
||||
<h1>This is my Count value : {count}</h1>
|
||||
<button onClick = {incrementCount}>increment</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Ue1
|
||||
```
|
||||
|
||||
Now the title will not be updated at every button click. This is because
|
||||
|
||||
**useEffect without dependency array, then it does:**
|
||||
- Component Did Mount
|
||||
- Component Did Update
|
||||
|
||||
**useEffect with dependency array( empty dependency array), then it do only:**
|
||||
- Component Did Mount
|
||||
|
||||
|
||||
`useEffect` with dependency array(empty dependency array) runs only once and only component mounting is there.
|
||||
|
||||
## ComponentMounting and ComponentUpdation based on events and values
|
||||
|
||||
1. Now we will create an input field and we will create `useState` for updating its value.
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
import {useState, useEffect} from 'react'
|
||||
function Ue1(){
|
||||
const[count, setCount] = useState(0)
|
||||
const[text, setText] = useState('')
|
||||
let incrementCount = () => {
|
||||
setCount(count + 1)
|
||||
}
|
||||
useEffect(() => {
|
||||
console.log('use Effect Runs')
|
||||
document.title = `Button clicked for ${count} times`
|
||||
})
|
||||
console.log('Other code that gets executed')
|
||||
return{
|
||||
<div>
|
||||
<h1>This is my Count value : {count}</h1>
|
||||
<input type = 'text' value = {text}></input>
|
||||
<button onClick = {incrementCount}>increment</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Ue1
|
||||
```
|
||||
|
||||
|
||||
2. We want that whenever we write anything inside this input field then it will be printed in `h2`. We will create `handleChange` to update the value of the text, and this will be called on the `onChange` of the input field.
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
import {useState, useEffect} from 'react'
|
||||
function Ue1(){
|
||||
const[count, setCount] = useState(0)
|
||||
const[text, setText] = useState('')
|
||||
let incrementCount = () => {
|
||||
setCount(count + 1)
|
||||
}
|
||||
let handleChange = (e) => {
|
||||
setText(e.target.value)
|
||||
}
|
||||
useEffect(() => {
|
||||
console.log('use Effect Runs')
|
||||
document.title = `Button clicked for ${count} times`
|
||||
})
|
||||
console.log('Other code that gets executed')
|
||||
return{
|
||||
<div>
|
||||
<h1>This is my Count value : {count}</h1>
|
||||
<input onChange = {handleChange} type = 'text' value = {text}></input>
|
||||
<h2>{text}</h2>
|
||||
<button onClick = {incrementCount}>increment</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Ue1
|
||||
```
|
||||
|
||||
|
||||
|
||||
- Here `useEffect` will run for every updation in the text field and for every button click.
|
||||
|
||||
|
||||
|
||||
|
||||
- Now if we want that `useEffect` will only run when we click on the button to increment the count value.
|
||||
- Then we can pass count in the dependency array, and then the `useEffect` will run only on the updation of the count value.
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
import React from 'react'
|
||||
import {useState, useEffect} from 'react'
|
||||
function Ue1(){
|
||||
const[count, setCount] = useState(0)
|
||||
const[text, setText] = useState('')
|
||||
let incrementCount = () => {
|
||||
setCount(count + 1)
|
||||
}
|
||||
let handleChange = (e) => {
|
||||
setText(e.target.value)
|
||||
}
|
||||
useEffect(() => {
|
||||
console.log('use Effect Runs')
|
||||
document.title = `Button clicked for ${count} times`
|
||||
},[count])
|
||||
console.log('Other code that gets executed')
|
||||
return{
|
||||
<div>
|
||||
<h1>This is my Count value : {count}</h1>
|
||||
<input onChange = {handleChange} type = 'text' value = {text}></input>
|
||||
<h2>{text}</h2>
|
||||
<button onClick = {incrementCount}>increment</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
export default Ue1
|
||||
```
|
@@ -0,0 +1,543 @@
|
||||
|
||||
|
||||
# IMDB Clone React Website
|
||||
|
||||
## Project Description
|
||||
|
||||
We will be creating an IMDB clone, where we will fetch Real-time trending movies data and will show it in grid format, we will be designing the whole application by using Tailwind CSS.
|
||||
|
||||
## Features of the project
|
||||
|
||||
The following will be the features of our IMDB clone:
|
||||
|
||||
- The user will be able to view all the latest & trneding movies (IMDB API)
|
||||
- User can create his own separate watchlist
|
||||
- User can filter movies according to genre
|
||||
- User can sort movies according to ratings
|
||||
- Pagination will be implemented to move from one page to another to get updated data of other movies
|
||||
- Search feature will be there for movies.
|
||||
- We will be deploying this to netlify
|
||||
|
||||
## Wireframe
|
||||
|
||||

|
||||
|
||||
|
||||
## Implementation
|
||||
|
||||
### What is tailwind css?
|
||||
|
||||
|
||||
Tailwind CSS is a highly popular and innovative utility-first CSS framework for web development. Unlike traditional CSS frameworks that provide pre-designed components, Tailwind CSS focuses on providing a comprehensive set of utility classes that make it easier to create custom and responsive designs without the need for writing custom CSS.
|
||||
|
||||
**Following are features of Tailwind CSS:**
|
||||
|
||||
1. **Utility-First Approach:** Tailwind CSS adopts a utility-first approach, offering a vast collection of small, single-purpose utility classes that can be combined to create complex designs and layouts. This approach promotes code reusability and rapid development.
|
||||
2. **Customizable and Configurable:** Tailwind is highly customizable through a configuration file, allowing developers to tailor the framework to match their project's specific design requirements. You can customize everything from colors and spacing to typography and breakpoints.
|
||||
3. **Responsive Design Made Easy:** Creating responsive web designs is simplified with Tailwind CSS. It provides responsive variants of utility classes, enabling developers to adapt the layout and styling of their websites for various screen sizes and devices effortlessly.
|
||||
4. **Performance Optimization:** Tailwind CSS is designed with performance in mind. It generates optimized CSS files by purging unused classes, resulting in smaller file sizes and faster loading times for web pages.
|
||||
5. **Active Community and Ecosystem:** Tailwind CSS has a thriving community of developers who contribute to its growth and share resources. Additionally, there are numerous plugins and extensions available that enhance Tailwind's capabilities, making it suitable for a wide range of web development projects.
|
||||
|
||||
In this application we will be integrating tailwind to our react app.
|
||||
|
||||
- **tailwind.confing.js**
|
||||
|
||||
```javascript=
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./src/**/*.{js,jsx,ts,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
```
|
||||
|
||||
### Src
|
||||
|
||||
- **index.js**:
|
||||
|
||||
- In this code snippet, we're diving into the world of web development with React, a popular JavaScript library for building user interfaces.
|
||||
- We start by importing some essential tools: `React` and `ReactDOM`. These are the building blocks of React applications. Think of them as the magic wand and canvas, respectively, for creating interactive web experiences.
|
||||
- We also import a CSS file, './index.css'. This file likely contains styles to make our IMDb web page look visually appealing and engaging.
|
||||
- The star of our show is the `App` component, which represents the heart and soul of our IMDb website. We import it from './App', indicating that this component will be responsible for displaying our movie and TV show information.
|
||||
- Now, let's set the stage. We create a special variable called `root` using `ReactDOM.createRoot(document.getElementById('root'))`. This variable acts as our spotlight, focusing on a specific area of the webpage where our IMDb content will appear.
|
||||
- Finally, it's showtime! We call `root.render(<App />)`, which is the grand moment when our `App` component takes center stage and begins rendering. This means it starts building the entire IMDb web page, showing movie posters, cast lists, and everything else our users want to see.
|
||||
|
||||
**Code**:
|
||||
|
||||
```javascript=
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(
|
||||
<App />
|
||||
);
|
||||
```
|
||||
|
||||
- **index.css**:
|
||||
|
||||
Certainly, here's the provided CSS code snippet with an explanatory introduction:
|
||||
|
||||
- **index.css**
|
||||
|
||||
- This CSS code snippet is all about styling our IMDb web page. CSS, or Cascading Style Sheets, is used to control the visual appearance of web content.
|
||||
- The code begins with `@tailwind` directives. These directives are used in conjunction with the Tailwind CSS framework. They help in defining the base styles, components, and utilities for our web page. Tailwind CSS is a utility-first CSS framework that makes it easy to style web applications.
|
||||
- Following the Tailwind directives, we have styles for the `body` element. These styles define how the main content of our IMDb webpage should look. Key aspects include:
|
||||
- Setting the margin to 0 to remove any default spacing around the page.
|
||||
- Specifying a font-family to ensure a consistent and readable text display on various devices and browsers.
|
||||
- Enabling font-smoothing and grayscale to enhance text readability and visual quality.
|
||||
- Next, there are styles for the `code` element. This is likely used for displaying code snippets or inline code within our IMDb web page. The specified font-family ensures that code is displayed in a monospace or fixed-width font, making it easy to read and distinguish from regular text.
|
||||
|
||||
**Code**:
|
||||
|
||||
```css=
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
```
|
||||
|
||||
- **App.js**:
|
||||
|
||||
Certainly, here's the code explanation for the provided `App.js` code:
|
||||
|
||||
- **App.js**
|
||||
|
||||
- This JavaScript file is a central part of our IMDb web application. It's where we define how the entire app works and what components it consists of.
|
||||
- We start by importing several components and libraries that our app depends on:
|
||||
- `import './App.css';` imports the CSS styles specific to our `App` component, which are likely used to style the layout and appearance of the app.
|
||||
- `Movies`, `WatchList`, `Navbar`, and `Banner` are components imported from their respective files. These components are used to build various parts of our IMDb app, such as displaying movies, the watchlist, the navigation bar, and a banner.
|
||||
- `BrowserRouter`, `Route`, and `Routes` are imported from 'react-router-dom'. These are essential for setting up client-side routing, allowing us to navigate between different pages in our app without a full page reload.
|
||||
- Inside the `App` function, we set up various pieces of functionality for our IMDb app:
|
||||
- We use `useState` to create two state variables, `watchList` and `pageNo`. These states help manage the user's watchlist and the current page number.
|
||||
- There are also functions like `handlePrev` and `handleNext` to handle pagination by changing the `pageNo` state when users click on previous or next buttons.
|
||||
- `handleAddToWatchList` and `handleRemoveFromWatchList` functions allow users to add or remove movies from their watchlist. These functions update the `watchList` state and store the updated watchlist in local storage.
|
||||
- The `useEffect` hook is used to retrieve the watchlist data from local storage when the component mounts, ensuring that users don't lose their watchlist when they revisit the app.
|
||||
- The `return` statement defines the structure of our IMDb app using the `BrowserRouter` and `Routes`. Inside the routes, we render various components based on the route path:
|
||||
- On the root path ('/'), we render the `Banner` and `Movies` components, passing them necessary props like `watchList`, `pageNo`, and functions for adding/removing from the watchlist.
|
||||
- On the '/watchlist' path, we render the `WatchList` component, providing it with the user's watchlist and functions to remove movies from it.
|
||||
- The entire `App` function represents the main structure of our IMDb web application, orchestrating the rendering of different components and handling user interactions.
|
||||
|
||||
**Code**:
|
||||
|
||||
```javascript=
|
||||
|
||||
import './App.css';
|
||||
import Movies from './Components/Movies';
|
||||
import WatchList from './Components/WatchList';
|
||||
import Navbar from './Components/Navbar';
|
||||
import Banner from './Components/Banner';
|
||||
import { BrowserRouter, Route, Routes } from 'react-router-dom';
|
||||
import {useState,useEffect} from "react"
|
||||
|
||||
|
||||
function App() {
|
||||
let [watchList,setWatchList] = useState([]);
|
||||
let [pageNo,setPageNo] = useState(1);
|
||||
|
||||
|
||||
let handlePrev = ()=>{
|
||||
if(pageNo>1)
|
||||
setPageNo(pageNo-1)
|
||||
}
|
||||
|
||||
let handleNext = ()=>{
|
||||
setPageNo(pageNo+1);
|
||||
}
|
||||
|
||||
let handleAddToWatchList = (movieObj)=>{
|
||||
// console.log("Inside add to watchlist");
|
||||
// console.log(movieId);
|
||||
// watchList.push(movieId); // it will not work since the reference is same
|
||||
|
||||
let newWatchList = [...watchList,movieObj];
|
||||
localStorage.setItem("movieApp",JSON.stringify(newWatchList));
|
||||
setWatchList(newWatchList);
|
||||
}
|
||||
|
||||
let handleRemoveFromWatchList = (movieObj)=>{
|
||||
let filteredWatchList = watchList.filter((movie)=>{
|
||||
return movie.id != movieObj.id;
|
||||
})
|
||||
localStorage.setItem("movieApp",JSON.stringify(filteredWatchList));
|
||||
setWatchList(filteredWatchList);
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
let moviesFromLocalStorage = localStorage.getItem("movieApp");
|
||||
if(!moviesFromLocalStorage){
|
||||
return;
|
||||
}
|
||||
setWatchList(JSON.parse(moviesFromLocalStorage));
|
||||
},[])
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Navbar/>
|
||||
|
||||
<Routes>
|
||||
<Route path='/' element={
|
||||
<>
|
||||
<Banner/>
|
||||
<Movies watchList={watchList}
|
||||
setWatchList={setWatchList}
|
||||
handleAddToWatchList={handleAddToWatchList}
|
||||
handleRemoveFromWatchList={handleRemoveFromWatchList}
|
||||
pageNo={pageNo}
|
||||
handleNext={handleNext}
|
||||
handlePrev={handlePrev}/>
|
||||
</>
|
||||
}></Route>
|
||||
|
||||
<Route path='/watchlist' element={
|
||||
<WatchList watchList={watchList}
|
||||
setWatchList={setWatchList}
|
||||
handleRemoveFromWatchList={handleRemoveFromWatchList}/>
|
||||
}
|
||||
></Route>
|
||||
|
||||
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
// <>
|
||||
// <Navbar/>
|
||||
// <Banner/>
|
||||
// <Movies/>
|
||||
// {/* <WatchList/> */}
|
||||
// </>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
```
|
||||
|
||||
|
||||
|
||||
- **App.css**
|
||||
|
||||
- This CSS code snippet is used to style elements within our IMDb web application. It defines various styles to ensure that the app looks visually appealing and functions correctly.
|
||||
- The `.App` class sets the text alignment to the center. This class is likely associated with the top-level element of our app.
|
||||
- `.App-logo` is used to style the IMDb logo. It sets the height to 40vmin (a relative unit based on the viewport size) and disables pointer events. This is often used for logos or images.
|
||||
- The `@media` query checks if the user prefers reduced motion. If not, it applies an animation to `.App-logo` to make it spin infinitely for 20 seconds in a linear fashion. This adds a dynamic touch to the logo when motion is allowed.
|
||||
- `.App-header` styles the header section of our IMDb app. It sets the background color, minimum height, and configures it to be a flex container with content centered both horizontally and vertically. The font size is adjusted relative to the viewport size, and the text color is set to white.
|
||||
- `.App-link` styles links in the app with a blue color (#61dafb). This is a common styling for clickable links.
|
||||
- Finally, `@keyframes App-logo-spin` defines an animation that smoothly rotates an element from 0 degrees to 360 degrees. This is used in conjunction with `.App-logo` to create the spinning effect of the IMDb logo.
|
||||
|
||||
**Code**:
|
||||
|
||||
```css=
|
||||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.App-link {
|
||||
color: #61dafb;
|
||||
}
|
||||
|
||||
@keyframes App-logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Utility
|
||||
|
||||
```javascript=
|
||||
|
||||
const genreids = {
|
||||
28: "Action",
|
||||
12: "Adventure",
|
||||
16: "Animation",
|
||||
35: "Comedy",
|
||||
80: "Crime",
|
||||
99: "Documentary",
|
||||
18: "Drama",
|
||||
10751: "Family",
|
||||
14: "Fantasy",
|
||||
36: "History",
|
||||
27: "Horror",
|
||||
10402: "Music",
|
||||
9648: "Mystery",
|
||||
10749: "Romance",
|
||||
878: "Sci-Fi",
|
||||
10770: "TV",
|
||||
53: "Thriller",
|
||||
10752: "War",
|
||||
37: "Western",
|
||||
};
|
||||
|
||||
export default genreids;
|
||||
```
|
||||
|
||||
#### Components
|
||||
|
||||
- **Banner.jsx**:
|
||||
|
||||
|
||||
- This JavaScript file defines the `Banner` component for our IMDb web application. The `Banner` typically appears at the top of the web page and often showcases a prominent image or information related to the content.
|
||||
- Inside the component function, we use the `useState` and `useEffect` hooks from React to manage component state and perform side effects.
|
||||
- `useState` initializes a state variable called `detail` with an initial value of `undefined`. This variable will hold data related to the movie we want to display in the banner.
|
||||
- The `useEffect` hook is used to make an HTTP GET request to the 'https://api.themoviedb.org/3/movie/upcoming' endpoint. This endpoint likely retrieves information about upcoming movies from a movie database. The API key '2816c138913c6ef73d40c883d36fbe56' is included in the request for authentication.
|
||||
- When the response is received successfully, it extracts data from the first movie in the results array and sets it to the `detail` state variable.
|
||||
- The `if` statement checks if `detail` is still undefined. If it is, the component returns nothing (undefined). This likely prevents the component from rendering until it has retrieved movie data from the API.
|
||||
- If `detail` has a value (i.e., movie data has been fetched), the component renders:
|
||||
- A `div` element with a class that sets the background image of the banner. The `url()` function in the `style` attribute dynamically sets the background image using the `poster_path` property from the `detail` data.
|
||||
- Inside this `div`, there's another `div` element that contains text. This is likely the movie title displayed on top of the banner image.
|
||||
- The styling includes making the text white, applying a semi-transparent gray background, and centering the text both horizontally and vertically. The `text-xl` class sets the font size to extra-large.
|
||||
- The `Banner` component fetches data about an upcoming movie and displays it as a banner on the IMDb webpage, creating an attractive and engaging visual element for users.
|
||||
|
||||
**Code**:
|
||||
|
||||
```javascript=
|
||||
import axios from "axios";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function Banner(){
|
||||
let [detail,setDetail] = useState(undefined)
|
||||
|
||||
useEffect(()=>{
|
||||
axios.get('https://api.themoviedb.org/3/movie/upcoming?api_key=2816c138913c6ef73d40c883d36fbe56')
|
||||
.then(function(res){
|
||||
// console.log(res);
|
||||
|
||||
// console.log(res.data.results[0].poster_path);
|
||||
let data = res.data.results[0]
|
||||
setDetail(data);
|
||||
})
|
||||
},[]);
|
||||
|
||||
if(detail == undefined){
|
||||
return
|
||||
}
|
||||
return(
|
||||
<div className="h-[20vh] md:h-[70vh] bg-cover bg-center flex items-end"
|
||||
style={{backgroundImage:`url(https://image.tmdb.org/t/p/original/${detail.poster_path})`}}>
|
||||
<div className="text-xl text-white bg-gray-900/60 w-full p-4 text-center ">
|
||||
{detail.title}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Banner;
|
||||
```
|
||||
|
||||
- **MovieCard.jsx**:
|
||||
|
||||
- This JavaScript file defines the `MovieCard` component. A `MovieCard` is a visual representation of a movie that users can interact with in our IMDb web application.
|
||||
- The component function takes several props:
|
||||
- `movieObj`: An object containing information about the movie, including its `id`.
|
||||
- `handleAddToWatchList`: A function to add the movie to the user's watchlist.
|
||||
- `handleRemoveFromWatchList`: A function to remove the movie from the user's watchlist.
|
||||
- `name`: The name or title of the movie.
|
||||
- `watchList`: An array representing the user's watchlist.
|
||||
- `poster_path`: A URL to the movie's poster image.
|
||||
- Inside the component, there's a function `isContain(movieObj)` that checks if the `movieObj` is already in the user's watchlist. It iterates through the `watchList` array and returns `true` if it finds a movie with the same `id`, indicating that the movie is in the watchlist. Otherwise, it returns `false`.
|
||||
- The component's `return` statement defines the structure and appearance of a `MovieCard`. It's a `div` element with the following features:
|
||||
- A fixed height and width (`h-[40vh]` and `w-[200px]`) to maintain a consistent size for movie cards.
|
||||
- The `bg-center` and `bg-cover` classes ensure that the background image (the movie poster) is centered and covers the entire card.
|
||||
- The `rounded-xl` class rounds the corners of the card, giving it a pleasant look.
|
||||
- On hover, the card slightly scales up (`hover:scale-110`) and the cursor changes to a pointer (`hover:cursor-pointer`), providing a visual cue that the card is interactive.
|
||||
- Inside the card, there's a dynamic icon that changes based on whether the movie is in the user's watchlist or not. If the movie is in the watchlist, it displays a "remove" icon (❌), and clicking it calls the `handleRemoveFromWatchList` function. If the movie is not in the watchlist, it displays an "add" icon (😁), and clicking it calls the `handleAddToWatchList` function.
|
||||
- Below the icon, there's a text element displaying the movie's name or title. It has a white text color, a semi-transparent gray background, and is centered both horizontally and vertically. The `text-xl` class sets the font size to extra-large.
|
||||
- This `MovieCard` component is a reusable card for displaying movie information and interacting with the user's watchlist. It provides a visually appealing and user-friendly way to add or remove movies from the watchlist.
|
||||
|
||||
**Code**:
|
||||
|
||||
```javascript=
|
||||
|
||||
export default function MovieCard(props){
|
||||
let {movieObj,handleAddToWatchList,handleRemoveFromWatchList,name,watchList,poster_path} = props;
|
||||
|
||||
function isContain(movieObj){
|
||||
for(let i=0;i<watchList.length;i++){
|
||||
if(watchList[i].id == movieObj.id){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return(
|
||||
<div className="h-[40vh] w-[200px] bg-center bg-cover
|
||||
rounded-xl hover:scale-110 duration-300 hover:cursor-pointer
|
||||
flex flex-col justify-between items-end overflow-hidden"
|
||||
style={{
|
||||
backgroundImage: `url(https://image.tmdb.org/t/p/original/${poster_path})`,
|
||||
}}>
|
||||
{isContain(movieObj)?
|
||||
<div onClick={()=>handleRemoveFromWatchList(movieObj)} className="m-4 bg-gray-900
|
||||
flex justify-center items-center
|
||||
h-8 w-8 rounded-lg">
|
||||
❌
|
||||
</div>
|
||||
:<div onClick={()=>handleAddToWatchList(movieObj)}
|
||||
className="m-4 bg-gray-900
|
||||
flex justify-center items-center
|
||||
h-8 w-8 rounded-lg">
|
||||
😍
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
<div className="text-xl text-white
|
||||
bg-gray-900/60 w-full p-2 text-center ">
|
||||
{name}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
- **Movies.jsx**
|
||||
|
||||
- This JavaScript file defines the `Movies` component, which is responsible for displaying a list of trending movies on our IMDb web application.
|
||||
- The component function takes several props:
|
||||
- `watchList` and `setWatchList`: These props are used to manage the user's watchlist.
|
||||
- `handleAddToWatchList` and `handleRemoveFromWatchList`: Functions for adding and removing movies from the watchlist.
|
||||
- `pageNo`: The current page number, which is used for pagination.
|
||||
- `handleNext` and `handlePrev`: Functions for navigating to the next and previous pages of trending movies.
|
||||
- Inside the component, there's a `useState` hook that initializes a state variable `movies` as an empty array. This variable will store the list of trending movies fetched from an API.
|
||||
- The `useEffect` hook is used to make an HTTP GET request to the 'https://api.themoviedb.org/3/trending/movie/day' endpoint. The `pageNo` prop is included in the request to retrieve a specific page of trending movies. The API key '2816c138913c6ef73d40c883d36fbe56' is used for authentication.
|
||||
- When the response is received successfully, it extracts the list of trending movies from `res.data.results` and sets it to the `movies` state variable.
|
||||
- The component's `return` statement defines the structure of the Movies component:
|
||||
- A title ("Trending Movies") displayed in a large and bold font at the top.
|
||||
- A flex container (`flex flex-wrap justify-around gap-8`) that arranges MovieCard components in a responsive grid layout. Each `MovieCard` represents a trending movie.
|
||||
- Inside the flex container, a `.map()` function is used to iterate through the `movies` array and generate a `MovieCard` component for each movie. The `key` prop is set to the movie's `id` for React's reconciliation.
|
||||
- The `MovieCard` component is passed various props, including movie information, watchlist data, and functions for adding and removing movies from the watchlist.
|
||||
- After displaying the list of movies, a `Pagination` component is rendered. This component allows users to navigate between pages of trending movies using the `pageNo`, `handleNext`, and `handlePrev` props.
|
||||
- The `Movies` component is responsible for rendering trending movies and providing user-friendly interactions for adding/removing movies from the watchlist and pagination.
|
||||
|
||||
**Code**:
|
||||
|
||||
```javascript=
|
||||
import MovieCard from "./MovieCard";
|
||||
import axios from "axios"
|
||||
import {useEffect, useState} from "react"
|
||||
import Pagination from "./Pagination";
|
||||
|
||||
function Movies(props){
|
||||
let {watchList,setWatchList,
|
||||
handleAddToWatchList,handleRemoveFromWatchList,
|
||||
pageNo,handleNext,handlePrev} = props;
|
||||
|
||||
let [movies,setMovies] = useState([]);
|
||||
|
||||
|
||||
useEffect(()=>{
|
||||
axios.get(`https://api.themoviedb.org/3/trending/movie/day?api_key=2816c138913c6ef73d40c883d36fbe56&page=${pageNo}`)
|
||||
.then(function(res){
|
||||
// console.log(res);
|
||||
// console.log(res.data.results);
|
||||
setMovies(res.data.results);
|
||||
})
|
||||
},[pageNo])
|
||||
|
||||
|
||||
|
||||
return(
|
||||
<div className="p-5">
|
||||
<div className="text-2xl m-5 font-bold text-center">
|
||||
Trending Movies
|
||||
</div>
|
||||
<div className="flex flex-wrap justify-around gap-8">
|
||||
{movies.map((movieObj)=>{
|
||||
// console.log(movieObj);
|
||||
return <MovieCard key={movieObj.id}
|
||||
movieObj={movieObj}
|
||||
name={movieObj.title}
|
||||
poster_path={movieObj.poster_path}
|
||||
watchList = { watchList}
|
||||
handleAddToWatchList = {handleAddToWatchList}
|
||||
handleRemoveFromWatchList = {handleRemoveFromWatchList}/>
|
||||
})}
|
||||
</div>
|
||||
<Pagination pageNo={pageNo}
|
||||
handleNext={handleNext}
|
||||
handlePrev={handlePrev}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Movies;
|
||||
```
|
||||
|
||||
|
||||
- **Navbar.jsx**
|
||||
|
||||
- This JavaScript file defines the `Navbar` component, which represents the navigation bar at the top of the IMDb web application. The navigation bar typically contains links to different sections or pages of the website.
|
||||
- Inside the component function, we use the `Link` component from the 'react-router-dom' library to create navigation links. This allows users to navigate between different sections of the IMDb website without a full page reload, providing a seamless user experience.
|
||||
- The component renders the following elements:
|
||||
- An `img` element with a source (`src`) that appears to be a base64-encoded image. This is likely the IMDb logo. The `alt` attribute is empty, indicating that this is a decorative image without alternative text.
|
||||
- A `Link` component with a `to` prop pointing to the root path ("/") of the application. This link represents the "Movies" section of the website. It has a large font size, a bold font-weight, and a hover effect that changes the cursor to a pointer, making it clear that it's clickable.
|
||||
- Another `Link` component with a `to` prop pointing to the "/watchlist" path. This link represents the "Watchlist" section of the website. Similar to the "Movies" link, it has a large font size, bold font-weight, and a hover effect.
|
||||
- The `Navbar` component provides essential navigation options, allowing users to easily switch between the "Movies" and "Watchlist" sections of the IMDb website, enhancing the overall user experience.
|
||||
|
||||
```javascript=
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
function Navbar(){
|
||||
return(
|
||||
<div className="flex items-center p-3">
|
||||
<img className="h-[50px] mx-6" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAMAAACahl6sAAAAflBMVEX///8AAACrq6tTU1MnJyd/f3/X19cEBAT29vaTk5P8/Pz5+fnGxsYJCQkXFxdkZGQsLCyampoyMjLNzc12dna+vr7p6em3t7ddXV1ra2s5OTkjIyPj4+McHBzw8PCioqJERERISEiHh4eUlJRxcXFNTU09PT2mpqaDg4MYGBiakx2lAAAFqklEQVR4nO2c6ZaiMBCFEVpww12x3de25/1fcJBuSVWCkoARzsz9fk2TAXOh6qYqeHQcAAAAAAAAAAAAAAAA+G+Y++uqp1CadfR92TQajc2uU/VUCrN2p4d+I2Va9XwK0PGv56+wIdGrelpGdCejz6WiIWFQ9dx0afVOg3GQqSFhWPUENdjO2ovhEw0JQavqaT5lHh29VY6EuifJOmom1qpLu+oJZ+Bza9VjUfWsGR1/l2GtWtQm259YqxZ1yPY8a9Wj2mzXslaJzaWZ5QKVZbuBtd4JPhbH2dxxzlmDVWT72jWz1phw+Tnq/aZBO/N/vDnbC1hrfz+9+qROd+ngOPWHt2V7MWsNr3LbNKPXWM2X6b/fkO3FrTWI5Gv59Dp93/lM/7Cb7a1eu4y1juTrrWluhfFDGKV/Wcv27ey4+DCc+MpjJtBUrjmmw258ZJL+ZSPbC1hrrOE42zoneki5x909HU5iqZs+7hdnewFrbdyWh21yNnOkQ1e++IIO//bp4hm9LNv963RvaK3BcNHubdMr9Ggmj5U7PKWn3h/XID3ygmwvZq1eu8fn6tPbsJrLn8LCbt9VjpbL9uLW+ilfak7TKnZWiYia3zB9jD1xrKiGctbqyZtqLepIoRLwLOw2Yplslcr2AtYaDNlzW8qf2jnQYVf+RBZ2IX1cw/SwWbbPo2/jqnU8iCs+FuIfSgYM6LCStyzsghkdWjw+6xHdqHn5Y6bhVrVOkrxkztpXNp6bdFjZAWVh17iyMVEM62a7a+atsYbdJE0EFuLhRL72iJ6pTIiH3VHc2cloIOpf3WyPDEQszzufJTMLcbUUZI60VxZCFnY/bpfVH2tm+7ChzVk+l4V4YycPT+jjEs5655ue7HVit3zQH2tlu6+vw5PP5SH+LQ+v6ePaKOmzoycH3hO3POkI0Y+sLzk0eIgrG+dzOrXw+UL4HKXqz+LREwnjTtR9Hhqs1jsoC+GSjHJnvTExKBuUm5BFR73gn0PTvQUC63bU0GDOqpSCHY8OX6XReWRglXsdHY5Db1zcPXxH9zVtS20gx1nVUvCTDh/JQFxMm+1TrDTfiopPXMxo9LBuRw0NFuJqKXikU/l1u1aBQjQYH3VLLXFn+cLLMkBxVr4QKv54pSfHdeStiDPdegyTCkhThUO74z09zLqdo3xSzkI4o3MOp8a9ZVI9KOtnDiLbQ3KUbfs9bzFUd/RLbL+H+/PVL/Z+XaxqItJZKai2GMwglFJwbfoAfukfpm6ZrzuIbE8zgWWA2mJc6McrpeDaoOi5s7k0o9Jf2RDZfg8hlgE5LQYrBdcF3q6tvGOkfEQhRLYvfw7wJlu5UazWG98d23cL7bXMlHqhOGIvLEySgTfZykLIar3N7V761/PeeHkYnEysVQ8x8dusWSmY02KErvl+kWguX46I+ZEjZcDzFsOU8EtuzF6LyPaBVAqqLYZhb59ys1atIrYMItvHvBRUWoyt6Tb8jdhaSy0P+pCdb5dmwIVGgbQhoMfrrFUPke1UR9piFHphLrbi38ggayZJi/F4Q+AxsbXK29jv4pQxnf7k9tLf8DFYtFYtellzMtVg2Vq1aJX7xkjyxrxiCb8UqFfJs/iwy/Ci//3fRf50K2Wv6x3ZXwOpEbpfm83K9loRaC6rraonmotShT+gSA31VpR3dg/w8i9VLRBSN8yFBHWijJCa1Bo/NCEEQuwAIRBiCQiBEEtACIRYAkIgxBIQAiGWgBAIsQSEQIglIARCLAEhEGIJCIEQS0AIhFgCQiDEEhACIZaAEAixBIRAiCUgBEIsASEQYgkIgRBLQAiEWAJCIMQSEAIhloAQCLFEGSEHr0Z8lBBSUyCkbkBI3dAVMs2/VLXorgoGP5ZbCWNNHdLPatUO9QfkHtMu81Nmlhkb6HCc1sytKbWqmgAAAAAAAAAAAAAAAP8SfwGtIGmvToPJOQAAAABJRU5ErkJggg==" alt="" />
|
||||
<Link to={"/"} className="text-3xl mx-6 font-bold text-sky-600 hover:cursor-pointer">Movies</Link>
|
||||
<Link to={"/watchlist"} className="mx-6 text-3xl font-bold text-sky-600 hover:cursor-pointer">WatchList</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Navbar;
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
@@ -0,0 +1,201 @@
|
||||
|
||||
|
||||
# IMDB Clone React Website
|
||||
|
||||
## Project Description
|
||||
|
||||
We will be creating an IMDB clone, where we will fetch Real-time trending movies data and will show it in grid format, we will be designing the whole application by using Tailwind CSS.
|
||||
|
||||
## Features of the project
|
||||
|
||||
The following will be the features of our IMDB clone:
|
||||
|
||||
- The user will be able to view all the latest & trneding movies (IMDB API)
|
||||
- User can create his own separate watchlist
|
||||
- User can filter movies according to genre
|
||||
- User can sort movies according to ratings
|
||||
- Pagination will be implemented to move from one page to another to get updated data of other movies
|
||||
- Search feature will be there for movies.
|
||||
- We will be deploying this to netlify
|
||||
|
||||
## Wireframe
|
||||
|
||||

|
||||
|
||||
|
||||
## Implementation
|
||||
|
||||
> **Note**: This lecture is in continuation to *'Full Stack LLD & Projects: React-4: IMDB Project- Part 1'*, please refer that lecture before hopping onto this one.
|
||||
|
||||
In this lecture we will be implementing the following features:
|
||||
|
||||
- Briefing Client side Routing and React Router
|
||||
- Watchlist Component
|
||||
- Pagination
|
||||
|
||||
|
||||
### Component
|
||||
|
||||
|
||||
- **WatchList.jsx**
|
||||
|
||||
- This JavaScript file defines the `WatchList` component, which represents the user's watchlist section in the IMDb web application. Users can view and manage the list of movies they have added to their watchlist.
|
||||
- The component function takes several props:
|
||||
- `watchList`: An array containing the user's watchlist of movies.
|
||||
- `handleRemoveFromWatchList`: A function to handle the removal of movies from the watchlist.
|
||||
- `setWatchList`: A function to update the watchlist state.
|
||||
|
||||
- The component also uses local state variables to manage filtering and searching of watchlist movies. These state variables include `genreList`, `currGenre`, and `search`.
|
||||
- Inside the component, there are several functions and effects that manage various aspects of the watchlist:
|
||||
- `hanldeFilter(genre)`: Updates the current genre filter when a genre is clicked.
|
||||
- `handleSearch(e)`: Updates the search query when the user types in the search input.
|
||||
- `sortIncreasing()`: Sorts the watchlist in increasing order of movie ratings.
|
||||
- `sortDecreasing()`: Sorts the watchlist in decreasing order of movie ratings.
|
||||
- The `useEffect` hook is used to populate the `genreList` state with unique genres based on the movies in the watchlist. It ensures that the genre filter options are updated whenever the watchlist changes.
|
||||
- The component's `return` statement defines the structure of the WatchList component:
|
||||
- Genre filter buttons are displayed at the top. Users can click on these buttons to filter the watchlist by genre.
|
||||
- A search input field allows users to search for movies in the watchlist based on their titles.
|
||||
- A table displays the watchlist movies, including columns for movie name, ratings, popularity, genre, and a delete option.
|
||||
- The table rows are generated based on the current genre filter and search query.
|
||||
- The ratings column headers allow users to sort the watchlist in ascending or descending order based on movie ratings.
|
||||
- The delete option in each row allows users to remove a movie from their watchlist.
|
||||
- The `WatchList` component provides essential functionality for users to manage and view their watchlist, making it a valuable feature of the IMDb web application.
|
||||
|
||||
**Code**:
|
||||
|
||||
```javascript=
|
||||
import { useEffect, useState } from "react";
|
||||
import genreids from "../Utility/genre";
|
||||
|
||||
function WatchList(props){
|
||||
let {watchList,handleRemoveFromWatchList,setWatchList} = props;
|
||||
let [genreList,setGenreList] = useState(["All Genres"]);
|
||||
let [currGenre,setCurrGenre] = useState("All Genres");
|
||||
let [search,setSearch] = useState("");
|
||||
|
||||
let hanldeFilter = (genre)=>{
|
||||
setCurrGenre(genre)
|
||||
}
|
||||
|
||||
let handleSearch = (e)=>{
|
||||
setSearch(e.target.value);
|
||||
}
|
||||
|
||||
let sortIncreasing = ()=>{
|
||||
let sorted = watchList.sort((movieA,movieB)=>{
|
||||
return movieA.vote_average-movieB.vote_average
|
||||
})
|
||||
setWatchList([...sorted]);
|
||||
}
|
||||
|
||||
let sortDecreasing = ()=>{
|
||||
let sorted = watchList.sort((movieA,movieB)=>{
|
||||
return movieB.vote_average-movieA.vote_average
|
||||
})
|
||||
setWatchList([...sorted]);
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
let temp = watchList.map((movieObj)=>{
|
||||
return genreids[movieObj.genre_ids[0]];
|
||||
})
|
||||
temp = new Set(temp);
|
||||
setGenreList(["All Genres",...temp]);
|
||||
},[watchList])
|
||||
|
||||
return(
|
||||
<>
|
||||
<div className="flex justify-center flex-wrap m-4">
|
||||
{genreList.map((genre)=>{
|
||||
return <div key={genre} onClick={()=>hanldeFilter(genre)} className={
|
||||
currGenre == genre?"hover:cursor-pointer flex justify-center items-center w-[9rem] h-[3rem] rounded-xl bg-blue-400 m-4 text-white font-bold "
|
||||
:"hover:cursor-pointer flex justify-center items-center w-[9rem] h-[3rem] rounded-xl bg-gray-400/50 m-4 text-white font-bold "}>{genre}</div>
|
||||
})}
|
||||
</div>
|
||||
|
||||
|
||||
<div className="flex justify-center my-4">
|
||||
<input onChange={handleSearch} value={search} className="h-[3rem] w-[18rem]
|
||||
border-none outline-none bg-gray-200
|
||||
px-4 text-lg " type="text" placeholder="Search for Movies" />
|
||||
</div>
|
||||
|
||||
|
||||
<div className="overflow-hidden rounded-lg shadow-md border border-gray-200 m-8">
|
||||
<table className="w-full text-gray-500 text-center ">
|
||||
<thead className="bg-gray-50 text-gray-900 border-b-2">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th className="flex">
|
||||
<div onClick={sortIncreasing} className="p-2"><i className="fa-solid fa-arrow-up"></i></div>
|
||||
<div className="p-2">Ratings</div>
|
||||
<div onClick={sortDecreasing} className="p-2"><i className="fa-solid fa-arrow-down"></i></div>
|
||||
</th>
|
||||
<th>Popularity</th>
|
||||
<th>Genre</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-gray-700">
|
||||
{watchList.filter((obj)=>{
|
||||
if(currGenre == "All Genres"){
|
||||
return true;
|
||||
}else{
|
||||
return genreids[obj.genre_ids[0]] == currGenre;
|
||||
}
|
||||
})
|
||||
.filter((movieObj)=>{
|
||||
return movieObj.title.toLowerCase().includes(search.toLocaleLowerCase());
|
||||
})
|
||||
.map((movieObj)=>{
|
||||
return <tr className="border-b-2">
|
||||
<td className="flex items-center px-6 py-4">
|
||||
<img className="h-[6rem] w-[10rem]" src={`https://image.tmdb.org/t/p/original/${movieObj.poster_path}`} alt="" />
|
||||
<div className="mx-4 ">{movieObj.title}</div>
|
||||
</td>
|
||||
<td>{movieObj.vote_average}</td>
|
||||
<td>{movieObj.popularity}</td>
|
||||
<td>{genreids[movieObj.genre_ids[0]]}</td>
|
||||
<td onClick={()=>handleRemoveFromWatchList(movieObj)} className=" text-red-600">Delete</td>
|
||||
</tr>
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default WatchList;
|
||||
```
|
||||
|
||||
|
||||
- **Pagination.jsx**:
|
||||
|
||||
- This JavaScript file defines the `Pagination` component, which is responsible for rendering pagination controls for navigating between pages of trending movies on the IMDb web application.
|
||||
- The component function takes three props as arguments:
|
||||
- `pageNo`: The current page number, indicating the page of trending movies being displayed.
|
||||
- `handleNext`: A function to handle the next page button click event.
|
||||
- `handlePrev`: A function to handle the previous page button click event.
|
||||
- The component renders the following elements:
|
||||
- A `div` element with a set of CSS classes to style the pagination container. It is flex-based, horizontally centered, and has a background color of gray (#808080). This container holds the pagination controls.
|
||||
- Inside this container, there are three `div` elements representing the pagination controls:
|
||||
1. The first `div` contains a left arrow icon, which is likely a visual cue for users to go to the previous page of trending movies. It has padding (`px-8`) for spacing and a hover effect that changes the cursor to a pointer when hovered over. The `onClick` event handler is set to `handlePrev`, indicating that clicking this element will trigger the `handlePrev` function.
|
||||
2. The second `div` displays the current page number (`pageNo`). It is styled with a bold font weight for emphasis.
|
||||
3. The third `div` contains a right arrow icon, which serves as a control for users to navigate to the next page of trending movies. Similar to the left arrow, it has padding, a hover effect, and an `onClick` event handler set to `handleNext`.
|
||||
- The `Pagination` component provides a simple and intuitive way for users to move between pages of trending movies. It enhances the user experience by allowing them to explore a variety of movie options conveniently.
|
||||
|
||||
**Code**:
|
||||
|
||||
```javascript=
|
||||
export default function Pagination({pageNo,handleNext,handlePrev}){
|
||||
|
||||
return(
|
||||
<div className="flex justify-center p-4 mt-8 items-center bg-gray-400">
|
||||
<div onClick={handlePrev} className="px-8 hover:cursor-pointer "><i className="fa-solid fa-arrow-left"></i></div>
|
||||
<div className="px-8 font-bold hover:cursor-pointer">{pageNo}</div>
|
||||
<div onClick={handleNext} className="px-8 hover:cursor-pointer"><i className="fa-solid fa-arrow-right"></i></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
@@ -0,0 +1,386 @@
|
||||
|
||||
|
||||
|
||||
|
||||
Today's agenda will primarily revolve around working on the watchlist component. However, before diving into the watchlist, we'll address a few tasks related to the movie component. In our current project, we've already implemented features like pagination and banners, showcasing various movies. Now, we're at the stage where we want to provide users with the ability to take actions, such as adding a movie to their watchlist.
|
||||
|
||||
At this moment, our watchlist is empty, so we'll focus on designing and developing this component.
|
||||
|
||||
|
||||
|
||||
Let's return to the code. We'd like to include an "Add to Watchlist" button for each movie card, as shown here:
|
||||
|
||||

|
||||
> Note to instructor - Help students understand and brainstorm this feature.
|
||||
|
||||
Now, let's navigate to the movies section and pick up from where we left off to add the watchlist functionality.
|
||||
|
||||
#### Pseudocode
|
||||
|
||||
```javascript=
|
||||
function Movies() {
|
||||
const [watchList, setWatchList] = useState([]);
|
||||
}
|
||||
```
|
||||
|
||||
We will start by initializing this as an empty array. One by one, we will add the IDs of all the movies, and then we will retrieve the movies based on their IDs.
|
||||
|
||||
**[Ask the leaners]**
|
||||
Now, how can we implement the process of clicking on a movie to obtain its ID and store it in the watchlist? Do you have any suggestions?
|
||||
|
||||
One approach is to use filtering based on the movie's ID. To begin, we will add a button to our movie card. Inside the card div, we will create another div to accommodate this button.
|
||||
|
||||
#### Pseudocode
|
||||
|
||||
```jsx=
|
||||
<div className="">
|
||||
😀
|
||||
</div>
|
||||
|
||||
```
|
||||
This div is intended for the button. For now, we're just adding an emoji. Let's proceed to style it.
|
||||
|
||||
#### Pseudocode
|
||||
|
||||
```jsx=
|
||||
<div className="p-2 bg-gray-900 rounded-xl absolute right-2 top-2">
|
||||
😀
|
||||
</div>
|
||||
|
||||
```
|
||||
If a movie has not been added to the watchlist, you should display an emoji. If it's already on the watchlist, you should display a cross sign. As of now, the watchlist is empty.
|
||||
|
||||
The purpose of adding a movie to the watchlist is that when we click on the emoji, a function should be triggered to provide us with the ID of the movie we've clicked on.
|
||||
|
||||
#### Pseudocode
|
||||
|
||||
```javascript=
|
||||
// watchList handlers
|
||||
|
||||
const addToWatchList = () => {
|
||||
const newWatchList = [...watchList, id];
|
||||
setWatchList(newWatchList);
|
||||
}
|
||||
|
||||
```
|
||||
Let's explore how the spread operator functions.
|
||||

|
||||
|
||||
> Note to instructor - Help students understand this.
|
||||
|
||||
Now, let's shift our focus back to our div. We are simply going to invoke this.
|
||||
|
||||
**[Ask the leaners]**
|
||||
What should be passed as the argument in this context?
|
||||
The movie ID should be passed as the argument here.
|
||||
|
||||
#### Pseudocode
|
||||
|
||||
```jsx=
|
||||
<div className="p-2 bg-gray-900 rounded-xl absolute right-2 top-2">
|
||||
<div onClick={() => addToWatchList(movie.id)}>
|
||||
😀
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```
|
||||
|
||||
We have wrapped the addToWatchList(movie.id) function call in an arrow function to ensure it's triggered correctly when clicked. So, when you click on this emoji, it will trigger a click event that, in turn, activates the "addToWatchList" function and retrieves the specific ID of the movie.
|
||||
|
||||
> Note to instructor - Feel free to test this functionality out.
|
||||
|
||||
To demonstrate how the spread operator works, we will create a "test.js" file and write the code accordingly.
|
||||
|
||||
#### Pseudocode
|
||||
|
||||
```javascript=
|
||||
let arr = [1, 2, 3, 4, 5];
|
||||
let updatedArr = [...arr, 6];
|
||||
console.log(updatedArr);
|
||||
|
||||
```
|
||||
|
||||
> Note to instructor - Feel free to test this and help students with the concept of spread operator.
|
||||
|
||||
Next, we will examine the functionality of a cross button for removing items from the watchlist, involving conditional code.
|
||||
|
||||
#### Pseudocode
|
||||
|
||||
```jsx=
|
||||
|
||||
<div className="p-2 bg-gray-900 rounded-xl absolute right-2 top-2">
|
||||
{watchList.includes(movie.id) === false ? (
|
||||
<div onClick={() => addToWatchList(movie.id)}>
|
||||
😀
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
❌
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
```
|
||||
|
||||
If the movie ID is not present in my watchlist, then for those movies, display the "Add to Watchlist" button. Otherwise, show the "Remove from Watchlist" button.
|
||||
|
||||
|
||||
At a high level, here are the steps we've taken:
|
||||
|
||||
* Added the "Add to Watchlist" button.
|
||||
* When clicked, the ID of that movie is added to the watchlist array.
|
||||
* Created the onClick event and the addToWatchList function.
|
||||
* Now, we also need a feature to remove movies from the watchlist.
|
||||
* To achieve this, we will create the removeFromWatchList function, which will filter out the specified ID from the watchlist.
|
||||
|
||||
|
||||
Now, we can proceed to create the "Remove from Watchlist" function.
|
||||
|
||||
#### Pseudocode
|
||||
|
||||
```javascript=
|
||||
// watchList handlers
|
||||
|
||||
const addToWatchList = (id) => {
|
||||
const newWatchList = [...watchList, id];
|
||||
setWatchList(newWatchList);
|
||||
}
|
||||
|
||||
const removeFromWatchList = (id) => {
|
||||
const filteredWatchList = watchList.filter((watchListId) => {
|
||||
return watchListId !== id;
|
||||
});
|
||||
setWatchList(filteredWatchList);
|
||||
}
|
||||
|
||||
```
|
||||
> Note to instructor - Feel free to test this.
|
||||
|
||||
|
||||
> Note to instructor - Feel free to wait and address any questions that students might have.
|
||||
|
||||
Now, let's write the code that allows us to remove a movie from our watchlist when we click on the cross button.
|
||||
|
||||
|
||||
#### Pseudocode
|
||||
|
||||
```jsx=
|
||||
<div className="p-2 bg-gray-900 rounded-xl absolute right-2 top-2">
|
||||
{watchList.includes(movie.id) === false ? (
|
||||
<div onClick={() => addToWatchList(movie.id)}>
|
||||
😀
|
||||
</div>
|
||||
) : (
|
||||
<div onClick={() => removeFromWatchList(movie.id)}>
|
||||
❌
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
```
|
||||
> Note to instructor - Feel free to test this.
|
||||
|
||||
|
||||
|
||||
> Note to instructor - Feel free to wait and address any questions that students might have.
|
||||
|
||||
**[Ask the leaners]**
|
||||
|
||||
We don't want these buttons to be shown by default and would like them to appear only when we hover over the movie cards. Do you have any suggestions for this?
|
||||
|
||||
-> In React, everything is controlled by the state. So, we can create a state to manage the visibility of these buttons.
|
||||
|
||||
#### Pseudocode
|
||||
|
||||
```javascript=
|
||||
function Movies() {
|
||||
const [hovered, setHovered] = useState('');
|
||||
}
|
||||
```
|
||||
|
||||
Now, we will create two functions:
|
||||
|
||||
|
||||
#### Pseudocode
|
||||
|
||||
```javascript=
|
||||
const showButton = (id) => {
|
||||
setHovered(id);
|
||||
}
|
||||
|
||||
const hideButton = (id) => {
|
||||
setHovered('');
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
We can create an additional div element for hovering over a movie.
|
||||
|
||||
#### Pseudocode
|
||||
|
||||
```jsx=
|
||||
<div
|
||||
onMouseEnter={() => showButton(movie.id)}
|
||||
onMouseLeave={() => hideButton()}
|
||||
>
|
||||
</div>
|
||||
```
|
||||
|
||||
Now, we'll apply CSS to show this element only when the movie ID matches.
|
||||
|
||||
#### Pseudocode
|
||||
```jsx=
|
||||
<div className="p-2 bg-gray-900 rounded-xl absolute right-2 top-2" style={{ display: hovered === movie.id ? 'block' : 'none' }}>
|
||||
{watchList.includes(movie.id) === false ? (
|
||||
<div onClick={() => addToWatchList(movie.id)}>
|
||||
😀
|
||||
</div>
|
||||
) : (
|
||||
<div onClick={() => removeFromWatchList(movie.id)}>
|
||||
❌
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
```
|
||||
|
||||
> Note to instructor - Wait for 5-10 minutes for the break
|
||||
|
||||
|
||||
|
||||
Now, let's proceed to the Watchlist component. Let's start by defining its visual design.
|
||||
|
||||
> Note to instructor - Help students understand and brainstorm this.
|
||||

|
||||
|
||||
|
||||
|
||||
#### Pseudocode
|
||||
|
||||
```jsx=
|
||||
function WatchList() {
|
||||
let movies = {
|
||||
// some movie details here
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="overflow-hidden rounded-lg border border-gray-200 shadow-md m-5">
|
||||
<table className="w-full border-collapse bg-white text-left text-sm text-gray-500">
|
||||
<thead>
|
||||
<tr className="bg-gray-50">
|
||||
<th className="px-6 py-4 font-medium text-gray-900">Name</th>
|
||||
<th>
|
||||
<div className="flex">
|
||||
<div>Ratings</div>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<div className="flex">
|
||||
<div>Popularity</div>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<div className="flex">
|
||||
<div>Genre</div>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-100 border-t border-gray-100">
|
||||
<tr className="hover:bg-gray-50">
|
||||
<td className="flex items-center px-6 py-4 font-normal text-gray-900">
|
||||
<img className="h-[6rem] w-[10rem] object-fit" src="" alt="" />
|
||||
<div className="font-medium text-gray-700 text-sm">Star Wars</div>
|
||||
</td>
|
||||
<td className="pl-6 py-4">
|
||||
7.8
|
||||
</td>
|
||||
<td className="pl-6 py-4">
|
||||
7.8
|
||||
</td>
|
||||
<td className="pl-2 py-4">
|
||||
Action
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default WatchList;
|
||||
|
||||
```
|
||||
|
||||
> Note to instructor - Please feel free to test if everything is functioning correctly up to this point.
|
||||
|
||||
Now, we'd like to incorporate this data. Inside the table body, We will utilize the movies array:
|
||||
|
||||
#### Pseudocode
|
||||
|
||||
```jsx=
|
||||
function WatchList() {
|
||||
let movies = [
|
||||
// Some movie details here
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="overflow-hidden rounded-lg border border-gray-200 shadow-md m-5">
|
||||
<table className="w-full border-collapse bg-white text-left text-sm text-gray-500">
|
||||
<thead>
|
||||
<tr className="bg-gray-50">
|
||||
<th className="px-6 py-4 font-medium text-gray-900">Name</th>
|
||||
<th>
|
||||
<div className="flex">
|
||||
<div>Ratings</div>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<div className="flex">
|
||||
<div>Popularity</div>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<div className="flex">
|
||||
<div>Genre</div>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-100 border-t border-gray-100">
|
||||
{movies.map((movie) => (
|
||||
<tr className="hover:bg-gray-50" key={movie.id}>
|
||||
<td className="flex items-center px-6 py-4 font-normal text-gray-900">
|
||||
<img className="h-[6rem] w-[10rem] object-fit" src="" alt="" />
|
||||
<div className="font-medium text-gray-700 text-sm">{movie.title}</div>
|
||||
</td>
|
||||
<td className="pl-6 py-4">
|
||||
{movie.vote_average}
|
||||
</td>
|
||||
<td className="pl-6 py-4">
|
||||
{movie.popularity}
|
||||
</td>
|
||||
<td className="pl-2 py-4">
|
||||
Action
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default WatchList;
|
||||
|
||||
```
|
||||
|
||||
|
||||
> Note to instructor - Input some data into the movies array and replace all the hardcoded data. Afterward, test this component to ensure it functions correctly.
|
||||
|
||||
|
||||
* Now, the WatchList component displays a list of movies.
|
||||
* It uses the movie array to populate the movie details.
|
||||
* Each movie is displayed within a table row (<tr>).
|
||||
* Movie data such as title, vote average, and popularity are shown in respective columns.
|
||||
* The code also includes styling classes for proper formatting and spacing.
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,643 @@
|
||||
# React 8 Prop Drilling and Context API
|
||||
|
||||
|
||||
### Agenda
|
||||
|
||||
1. We will understand the problem of prop drilling
|
||||
2. We will overcome prop drilling by using context API
|
||||
3. We will discuss useRef hook
|
||||
4. We will discuss useMemo hook(you should have a basic knowledge of memorisation)
|
||||
|
||||
|
||||
### Prop drilling
|
||||
|
||||
When you have a piece of specific information that is required at a specific component but to reach that specific component you will have to go through all the components that are one level up the hierarchy.
|
||||
|
||||
To understand this in a better way let's take an example of a family tree where the family wants to pass on information to their granddaughter but they will have to pass the information first to the parents then to the children and in the end, the information will reach the granddaughter as shown in the image.
|
||||
|
||||

|
||||
|
||||
Now if we replace the family with the components the tree will look something like this.
|
||||
|
||||

|
||||
|
||||
In the above image, the prompt: "message:'Hi'" should reach component E but it will have to go through component B and component D as well. Here, one level up is Component D then Component B and then Component A. So, this problem is known as prop drilling.
|
||||
|
||||
|
||||
**Example**
|
||||
|
||||
|
||||
To understand the issue with prop drilling, let's create a React APP with the name Context and then create one folder named "prop_drill" inside the source (src) folder. Inside the folder prop_drill, we will add all the components that are going to be used to replicate the family tree we saw above as an example. Inside the prop_drill folder make five files named 'family.js', 'parents.js', 'children.js', 'grandson.js' and 'granddaughter.js'.
|
||||
|
||||
First, create a `family.js` component by importing the react function component export.
|
||||
|
||||
```javascript!
|
||||
import React from 'react';
|
||||
|
||||
function Family(){
|
||||
return{
|
||||
<div>Family</div>
|
||||
}
|
||||
}
|
||||
|
||||
export default Family
|
||||
```
|
||||
|
||||
|
||||
Now, add the family component inside the `app.js` file.
|
||||
|
||||
```javascript!
|
||||
import logo from './logo.svg';
|
||||
import './App.css';
|
||||
import Family from './prop_drill/Family';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="App">
|
||||
<Family/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
```
|
||||
|
||||
In the app.css file, simple CSS code is added to make the hierarchical family tree.
|
||||
|
||||
```css!
|
||||
/*app.css file */
|
||||
.family{
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.parent{
|
||||
border: 5px solid #000;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.children{
|
||||
border: 3px solid #ff5722;
|
||||
padding: 0.5rem 0 0 1em;
|
||||
}
|
||||
|
||||
.gson{
|
||||
border: 2px solid #3f51b5;
|
||||
padding: 0.5rem 0 0 1.5rem;
|
||||
margin: 0.2rem;
|
||||
}
|
||||
|
||||
.gdaughter{
|
||||
border: 2px solid #e929ad;
|
||||
padding: 0.5rem 0 0 1.5rem;
|
||||
margin: 0.2rem;
|
||||
}
|
||||
```
|
||||
|
||||
Now, we will create a class of family inside the family.js file and also import the Parent file.
|
||||
|
||||
```javascript!
|
||||
import Parent from './Parent';
|
||||
|
||||
const Family = () => {
|
||||
return (
|
||||
<div className='family'>
|
||||
<Parent/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Family
|
||||
```
|
||||
|
||||
Add the following program in the Children.js file.
|
||||
|
||||
```javascript!
|
||||
import GrandDaughter from "./GrandDaughter";
|
||||
import GrandSon from "./GrandSon";
|
||||
|
||||
|
||||
const Children = () => {
|
||||
|
||||
return(
|
||||
<div className="children">
|
||||
<h2>Children</h2>
|
||||
<GrandSon/>
|
||||
<GrandDaughter/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Children;
|
||||
```
|
||||
|
||||
After executing the above programs when we run the `npm start` command for our app the result will look like this:
|
||||
|
||||

|
||||
|
||||
Now let's create the remaining two files of grandson and granddaughter as well:
|
||||
|
||||
```javascript!
|
||||
// grandson.js
|
||||
const GrandSon = () => {
|
||||
|
||||
return(
|
||||
<div className="gson">
|
||||
GrandSon
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default GrandSon;
|
||||
```
|
||||
|
||||
```javascript!
|
||||
//granddaughter.js
|
||||
const GrandDaughter = () => {
|
||||
|
||||
return(
|
||||
<div className="gdaughter">
|
||||
GrandDaughter
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default GrandDaughter;
|
||||
```
|
||||
|
||||
As you can see in app.js we have added a parent component and in parent.js we have added a children component. Same way to create heirarchy we have added both grandson and granddaughter in children.js.
|
||||
|
||||
The result will look something like this after executing the code:
|
||||
|
||||

|
||||
|
||||
|
||||
Now, let's pass the information from one component to the granddaughter component. For that, create an object in the app.js file inside the function or outside the function.
|
||||
|
||||
```javascript!
|
||||
const familyInfo = {
|
||||
familyName: "The Griffins",
|
||||
|
||||
onlyForParents : () => {
|
||||
console.log("Info for Parents");
|
||||
},
|
||||
|
||||
onlyForGrandChildren : () => {
|
||||
console.log("Info for GrandChildren");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**[Ask the learners]**
|
||||
How will you pass the familyInfo object in the family component?
|
||||
-> **By passing prompts** We will pass info prompt inside we will pass familyInfo.
|
||||
|
||||
```javascript!
|
||||
function App() {
|
||||
return (
|
||||
<div className="App">
|
||||
<Family info={familyInfo}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
Now, the family component will have access to the familyInfo. For that, we need to destructure the info prompt from app.js.
|
||||
|
||||
```javascript!
|
||||
const Family = ({info}) => {
|
||||
return (
|
||||
<div className='family'>
|
||||
<Parent/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Now, let's console log parentInfo.familyName from the parent.js file.
|
||||
|
||||
```javascript!
|
||||
import Children from './Children'
|
||||
|
||||
const Parent = ({parentInfo}) => {
|
||||
console.log('This is from parents:',parentInfo);
|
||||
return (
|
||||
<div className="parent">
|
||||
<h1>{`Parent ${parentInfo.familyName}`}</h1>
|
||||
<Children/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Parent;
|
||||
```
|
||||
|
||||
The result will look something like this:
|
||||
|
||||

|
||||
|
||||
Let's invoke `onlyForParents` in Parent.js.
|
||||
|
||||
```javascript!
|
||||
<p>{parentInfo.onlyForParents()}</p>
|
||||
```
|
||||
To show the result on the screen, remove console.log from onlyForParents and also from onlyForGrandChildren then add a return statement.
|
||||
|
||||
**Result**
|
||||
|
||||

|
||||
|
||||
The information from parents is now to be passed on to Children. To do that the same steps are followed from family to parents.
|
||||
|
||||
```javascript!
|
||||
const Children = ({childrenInfo}) => {
|
||||
|
||||
return(
|
||||
<div className="children">
|
||||
<h2>{`Children ${childrenInfo.familyName}`}</h2>
|
||||
<GrandSon/>
|
||||
<GrandDaughter/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
The family name is to be passed on to the grandchildren. It will also follow the same process.
|
||||
|
||||
`grandSon.js`
|
||||
|
||||
```javascript!
|
||||
const GrandSon = ({grandSonInfo}) => {
|
||||
|
||||
return(
|
||||
<div className="gson">
|
||||
<h3>{`GrandSon ${grandSonInfo.familyName}`}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default GrandSon;
|
||||
```
|
||||
|
||||
`grandDaughter.js`
|
||||
|
||||
```javascript!
|
||||
const GrandDaughter = ({grandDaughterInfo}) => {
|
||||
|
||||
return(
|
||||
<div className="gdaughter">
|
||||
<h3>{`GrandDaughter ${grandDaughterInfo.familyName}`}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default GrandDaughter;
|
||||
```
|
||||
|
||||
`children.js`
|
||||
|
||||
```javascript!
|
||||
import GrandDaughter from "./GrandDaughter";
|
||||
import GrandSon from "./GrandSon";
|
||||
|
||||
|
||||
const Children = ({childrenInfo}) => {
|
||||
|
||||
return(
|
||||
<div className="children">
|
||||
<h2>{`Children ${childrenInfo.familyName}`}</h2>
|
||||
<GrandSon grandSonInfo = {childrenInfo}/>
|
||||
<GrandDaughter grandDaughterInfo = {childrenInfo}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Children;
|
||||
```
|
||||
|
||||
**Result**
|
||||
|
||||

|
||||
|
||||
Passing the information onlyForGrandChildren.
|
||||
|
||||
`grandSon.js`
|
||||
```javascript!
|
||||
<div className="gson">
|
||||
<h3>{`GrandSon ${grandSonInfo.familyName}`}</h3>
|
||||
<p>{grandSonInfo.onlyForGrandChildren()}</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
`grandDaughter.js`
|
||||
|
||||
```javascript!
|
||||
<div className="gdaughter">
|
||||
<h3>{`GrandDaughter ${grandDaughterInfo.familyName}`}</h3>
|
||||
<p>{grandDaughterInfo.onlyForGrandChildren()}</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Result**
|
||||
|
||||

|
||||
|
||||
As we can see the information was passed on from one component to another. Here, the components were very few. If the components are more than 10 it will not be a tedious task and will be more prone to errors.
|
||||
|
||||
*To overcome this, the concept of Context API was introduced.*
|
||||
|
||||
|
||||
|
||||
### Context API
|
||||
|
||||
Context is a global state that can have the data the developer wants to pass to all the other components.
|
||||
|
||||
Let's understand it with the same example of family.
|
||||
So, in a family where we were passing the information from component to component, we will create a context in component A i.e., family. The context will contain the message which will be available globally and could be accessed by any component without passing from one component to another.
|
||||
|
||||

|
||||
|
||||
The context works as a provider and all the other components will work as consumers.
|
||||
|
||||
**Example**
|
||||
|
||||
Let's use the example of family and code using context API. For that, create a new folder inside your source folder with the name ContextComponents (the same way as already done in the above example of prop drilling). Inside this folder create the same files as were already created inside prop_drill.
|
||||
|
||||
Also, copy the code for all the js files created which will be rendered.
|
||||
|
||||
We will create a context for the family as it is the topmost component after `app.js`. For that, create a new file `FamilyContext.js` where the context will be created. To create a context there is already an inbuilt method named `createContext` in React. The FamilyContext can be imported into your app.js file.
|
||||
|
||||
```javascript!
|
||||
//FamilyContext.js
|
||||
import React, { createContext } from "react";
|
||||
|
||||
//the variable familycontext becomes a context
|
||||
export const FamilyContext = React.createContext()
|
||||
```
|
||||
|
||||
After importing FamilyContext in your app.js file, we can actually wrap the FamilyC component inside the FamilyContext. And we can pass down the value by just passing the value property inside the FamilyContext tag. Also, remove prompts from files. We have to add a provider with the context as it is a provider and the other components are consumer.
|
||||
|
||||
`ParentsC.js`
|
||||
|
||||
```javascript!
|
||||
import ChildrenC from './ChildrenC';
|
||||
import { FamilyContext } from './FamilyContext';
|
||||
import { useContext } from 'react';
|
||||
|
||||
const ParentsC = () => {
|
||||
const parentInfo = useContext(FamilyContext);
|
||||
console.log(parentInfo);
|
||||
return (
|
||||
<div className="parent">
|
||||
<h1>{`Parent ${parentInfo.familyName}`}</h1>
|
||||
<p></p>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ParentsC;
|
||||
```
|
||||
|
||||
`app.js`
|
||||
|
||||
```javascript!
|
||||
function App() {
|
||||
return (
|
||||
<div className="App">
|
||||
<FamilyContext.Provider value = {familyInfo}>
|
||||
<FamilyC/>
|
||||
</FamilyContext.Provider>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
After the console log the result comes out using the context.
|
||||
|
||||

|
||||
|
||||
In the same way, it can be done for both children and grandChildren.
|
||||
|
||||
`GrandSon.js`
|
||||
```javascript!
|
||||
import { useContext } from "react";
|
||||
import { FamilyContext } from "./FamilyContext";
|
||||
const GrandSonC = () => {
|
||||
let grandSonInfo = useContext(FamilyContext)
|
||||
return(
|
||||
<div className="gson">
|
||||
<h3>{`GrandSon ${grandSonInfo.familyName}`}</h3>
|
||||
<p></p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default GrandSonC;
|
||||
```
|
||||
|
||||
`GrandDaughter.js`
|
||||
|
||||
```javascript!
|
||||
import { useContext } from "react";
|
||||
import { FamilyContext } from "./FamilyContext";
|
||||
|
||||
const GrandDaughterC = () => {
|
||||
let grandDaughterInfo = useContext(FamilyContext)
|
||||
return(
|
||||
<div className="gdaughter">
|
||||
<h3>{`GrandDaughter ${grandDaughterInfo.familyName}`}</h3>
|
||||
<p>{grandDaughterInfo.onlyForGrandChildren()}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default GrandDaughterC;
|
||||
```
|
||||
|
||||
`Children.js`
|
||||
|
||||
```javascript!
|
||||
import { FamilyContext } from "./FamilyContext";
|
||||
import { useContext } from "react";
|
||||
import GrandSonC from "./GrandSonC";
|
||||
import GrandDaughterC from "./GrandDaughterC";
|
||||
const ChildrenC = () => {
|
||||
const childrenInfo = useContext(FamilyContext)
|
||||
return(
|
||||
<div className="children">
|
||||
<h2>{`Children ${childrenInfo.familyName}`}</h2>
|
||||
<GrandSonC/>
|
||||
<GrandDaughterC/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ChildrenC;
|
||||
```
|
||||
|
||||
**Result**
|
||||
|
||||

|
||||
|
||||
As we can see, we got the same results using both prop drilling and context API. But, in Context API we didn't use any prompts instead we just passed the context by importing in our components.
|
||||
|
||||
> The context is an orchestrator or a centralized place where related elements within a hierarchy can access data from another element within the same hierarchical scope.
|
||||
|
||||
You can go through this [article](https://www.smashingmagazine.com/2020/01/introduction-react-context-api/) for more information.
|
||||
|
||||
|
||||
|
||||
### useRef Hook
|
||||
|
||||
Let's understand what is useRef and why is it important using an example.
|
||||
|
||||
Create a new component and name it Ref inside your ContextComponents folder.
|
||||
|
||||
`Ref.js`
|
||||
```javascript!
|
||||
import React, { useState } from "react";
|
||||
|
||||
function Ref() {
|
||||
const [name, setName] = useState("Alex");
|
||||
|
||||
function clear(){
|
||||
setName('')
|
||||
}
|
||||
return(
|
||||
<>
|
||||
<h1>UseRef</h1>
|
||||
<input type="text" value={name} onChange = {(e) => setName(e.target.value)}>
|
||||
|
||||
</input>
|
||||
<button onClick={() => clear()}> Clear</button>
|
||||
</>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
export default Ref;
|
||||
```
|
||||
|
||||
Add this Ref component in the App.js file (comment out the div element).
|
||||
|
||||
**Result**
|
||||
|
||||

|
||||
|
||||
In the result, we see that a blue outline is visible at the border of the text box even though there is no code added. But as soon as we click on the clear button, the blue border is also removed, basically all the focus is removed from the text box. Here, we do not want the focus should be moved from the input box or textbox.
|
||||
|
||||
UseRef Hook allows you to access DOM properties inside React which returns an object. The object contains a key known as "current" where you can set anything inside it. This key will allow you to manipulate that particular element with respect to the DOM.
|
||||
|
||||
So in the above code, we will use the useRef hook. This useRef will allow you to get the input object inside the `current` key. In HTML, we have access to the `ref` attribute which stands for Reference.
|
||||
|
||||
```javascript!
|
||||
function Ref() {
|
||||
const [name, setName] = useState("Alex");
|
||||
|
||||
function clear(){
|
||||
setName('')
|
||||
RefElement.current.focus()
|
||||
}
|
||||
|
||||
let RefElement = useRef('')
|
||||
console.log(RefElement);
|
||||
return(
|
||||
<>
|
||||
<h1>UseRef</h1>
|
||||
<input ref ={RefElement} type="text" value={name} onChange = {(e) => setName(e.target.value)}>
|
||||
|
||||
</input>
|
||||
<button onClick={() => clear()}> Clear</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
In the above code, we are setting the key default input as an empty string for the `current` key. The reference is set for the input field. As we don't want to move the focus from the input field we will add the RefElement in the clear() function and use the DOM method focus().
|
||||
|
||||
**Result**
|
||||
|
||||

|
||||
|
||||
In the result, we can see even if we click on the clear button the focus from the input field is not moved.
|
||||
|
||||
Let's take another example where we want to change the text colour as well.
|
||||
|
||||
To do that create another button and name it Change Color. Make a function with the name changeColor() add the RefElement we created and use the DOM properties for changing the color.
|
||||
|
||||
```javascript!
|
||||
function changeColor(){
|
||||
RefElement.current.style.color = 'Pink'
|
||||
}
|
||||
|
||||
<button onClick={() => changeColor()}> Change Color</button>
|
||||
```
|
||||
|
||||
**Result**
|
||||
|
||||

|
||||
|
||||
You can read the following article on [Medium](https://medium.com/trabe/react-useref-hook-b6c9d39e2022) to understand useRef Hook better.
|
||||
--
|
||||
|
||||
### useMemo
|
||||
|
||||
In dynamic programming, there's a step called Memoization. Memoization means to have a temporary memory area where things get stored so that no function calls are made instead we can go there and get the value and use that value. Same way it works for React too.
|
||||
|
||||
Let's take a very simple example where we will get the factorial of numbers.
|
||||
Create a new file named `Factorial.js` inside the ContextComponents folder.
|
||||
|
||||
```javascript!
|
||||
import React, { useState } from 'react'
|
||||
|
||||
function Factorial() {
|
||||
|
||||
const [number, setNumber] = useState(1)
|
||||
const [inc, setInc] = useState(0)
|
||||
|
||||
const onChange = (event) =>{
|
||||
setNumber(Number(event.target.value))
|
||||
}
|
||||
|
||||
const onClick = () => {
|
||||
setInc(i=>i+1)
|
||||
}
|
||||
let factorial = factorialOf(number)
|
||||
return (
|
||||
<div>
|
||||
<span>Factorial of - </span>
|
||||
<input type= 'number' value={number} onChange={onChange}></input>
|
||||
<span>Factorial Value: {factorial} </span>
|
||||
|
||||
<button onClick={onClick}>re-render</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function factorialOf(n){
|
||||
console.log('Factorial function called: ')
|
||||
|
||||
return n<=0 ?1: n* factorialOf(n-1)
|
||||
}
|
||||
|
||||
export default Factorial
|
||||
```
|
||||
|
||||
The above code is not optimised as there are so many function calls when we try to increase the value. Why? Because with each increasing number, we are calling the function again and again. Also, the number of function calls increases while clicking on re-render as you can see in the image.
|
||||
|
||||

|
||||
|
||||
The re-render button takes the input number and calculates the factorial again because we do not have the value of the factorial already set. That means on clicking the button it calls the function again and again which is not optimised. To overcome this, we will use **useMemo Hook** which is similar to `useEffect` where we do not try to update the state again and again. We update the state only when there's a dependency.
|
||||
|
||||
Similarly, there's `useMemo` that is used to memoize the function. It accepts the function that you need to memoize and also comes with the condition of when you want to call the function.
|
||||
|
||||
In the above example, we need to memoize the factorial function. To do that use the `useMemo` hook.
|
||||
|
||||
```javascript!
|
||||
const factorial = useMemo(() => factorialOf(number),[number])
|
||||
```
|
||||
|
||||
After running the program, we see that even if we click on the re-render button the function is not called. It is only called when changing the values. The value that was already there is being memoized.
|
||||
|
||||
You can refer to this [article](https://dmitripavlutin.com/react-usememo-hook/) to understand useMemo Hook.
|
@@ -0,0 +1,452 @@
|
||||
# React Redux and Redux Toolkit
|
||||
|
||||
|
||||
### Redux
|
||||
|
||||
Redux is a management tool used to manipulate data within creating a store from where each component can access the properties by dispatching actions to reducers.
|
||||
|
||||
**Example**
|
||||
|
||||
Let's take an example to understand.
|
||||
|
||||
Initialise a react app you can name it `Redux app`.
|
||||
|
||||
We will be creating a replica of an E-commerce shopping cart application using Redux.
|
||||
|
||||
First, let's create two pages - the home page and the cart page.
|
||||
|
||||
You can have two pages using React Router (allows page routing). First, Install React Router then Create a nav bar and add two links: for the home page and cart page.
|
||||
|
||||
To install React Router run the command `npm install react-router-dom
|
||||
|
||||
The very first step after installing the react router is to **import browser router** then routes and then every specific route.
|
||||
|
||||
```javascript!
|
||||
import { BrowserRouter, Routes, Route } from 'react-router-dom';
|
||||
```
|
||||
|
||||
Step 1: Create a Home page
|
||||
|
||||
```javascript!
|
||||
import React from "react";
|
||||
|
||||
const Home = () => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>This is Home page.</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home
|
||||
```
|
||||
|
||||
Step 2: Create a Cart page
|
||||
|
||||
```javascript!
|
||||
import React from 'react'
|
||||
|
||||
const Cart = () => {
|
||||
return (
|
||||
<div>
|
||||
<h1>This is Cart page.</h1>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Cart
|
||||
```
|
||||
|
||||
In the `app.js` file import Browser router, routes and route and add paths to both the pages.
|
||||
|
||||
```javascript!
|
||||
import logo from './logo.svg';
|
||||
import './App.css';
|
||||
import { BrowserRouter, Routes, Route } from 'react-router-dom'
|
||||
import Cart from './pages/Cart';
|
||||
import Home from './pages/Home';
|
||||
import NavBar from './components/NavBar';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="App">
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path = '/' element = {<Home/>}></Route>
|
||||
<Route path = '/cart' element = {<Cart/>}></Route>
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
```
|
||||
|
||||
Now, we want to create a nav bar for our app. To do that create a folder named components inside your source folder. Create a file named NavBar.js inside that folder.
|
||||
After that add the following simple CSS and Links to your pages.
|
||||
|
||||
`NavBar.js`
|
||||
|
||||
```javascript!
|
||||
import React from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
const NavBar = () => {
|
||||
return (
|
||||
<div
|
||||
style = {{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between'
|
||||
}}>
|
||||
<span className='logo'>Redux Store</span>
|
||||
<div>
|
||||
<Link className='navLink' to = '/'>Home</Link>
|
||||
<Link className='navLink' to='/cart'>Cart</Link>
|
||||
<span className='cartCount'>Cart items:0</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default NavBar
|
||||
```
|
||||
|
||||
Add your NavBar to your app.js inside the `<BrowserRouter></BrowserRouter>` tag.
|
||||
|
||||
Add some CSS inside the `index.css` file to make your page look more optimised.
|
||||
|
||||
`index.css`
|
||||
|
||||
```css!
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto',
|
||||
'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans',
|
||||
'Helvetica Neue', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
padding: 20px;
|
||||
background: whitesmoke;
|
||||
}
|
||||
img {
|
||||
height: 80px;
|
||||
}
|
||||
.card {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.productsWrapper{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 30px;
|
||||
}
|
||||
.btn{
|
||||
border: none;
|
||||
outline: none;
|
||||
background: gold;
|
||||
padding: 5px 10px;
|
||||
color: black;
|
||||
border-radius: 8px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.btn:hover{
|
||||
background: green;
|
||||
}
|
||||
|
||||
.remove-btn{
|
||||
border: none;
|
||||
outline: none;
|
||||
background: gold;
|
||||
padding: 5px 10px;
|
||||
color: black;
|
||||
border-radius: 8px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.remove-btn:hover{
|
||||
background-color: red;
|
||||
}
|
||||
.heading{
|
||||
padding: 25px 0;
|
||||
}
|
||||
.cartCount{
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
margin-left: 40px;
|
||||
}
|
||||
.navLink{
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.cartCard{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #fff;
|
||||
margin-bottom: 20px;
|
||||
padding: 14px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
```
|
||||
|
||||
Now, to create our shopping cart application we will be using [fakeStoreAPI](https://fakestoreapi.com) from where we can get some product data.
|
||||
|
||||
How to get data from an API?
|
||||
-> The package that we are going to use is **Axios**. But Fetch can also be used to get data from an API.
|
||||
|
||||
Fresh applications do not have access to Axios. Therefore, you have to install it using the command `npm install axios`.
|
||||
|
||||
By using axios, you can use Promises or use the fetch API or async await.
|
||||
|
||||
We will create a separate component named `Products.js` inside your components folder to get the data pick that component and add it to our home page.
|
||||
|
||||
We use two Hooks: **useEffect** to make asynchronous calls to your API and **useState** for maintaining a state.
|
||||
|
||||
Function is defined as async and whenever you do something that will take time you use the await.
|
||||
By using useEffect hook we can call for our API.
|
||||
|
||||
This is your API endpoint: `'https://fakestoreapi.com/products'`
|
||||
|
||||
`Products.js`
|
||||
```javascript!
|
||||
import axios from 'axios'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
const Products = () => {
|
||||
|
||||
const [products, setProducts] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
const getProducts = async () => {
|
||||
const res = await axios.get('https://fakestoreapi.com/products')
|
||||
console.log(res)
|
||||
}
|
||||
|
||||
getProducts()
|
||||
},[])
|
||||
|
||||
return (
|
||||
<div></div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Products
|
||||
```
|
||||
We want this data to be added to our Home page. To do that add the Products component inside Home.js.
|
||||
|
||||
To display all the products from this API we will use `setProducts(res.data)` and create a new div inside which we will add all the components we want to display like product image, and price and add to the cart button.
|
||||
|
||||
`Products.js`
|
||||
|
||||
```javascript!
|
||||
return (
|
||||
<div>
|
||||
|
||||
<div className='productsWrapper'>
|
||||
{
|
||||
products.map((product) => {
|
||||
return <div className='card'>
|
||||
<img src= {product.image}/>
|
||||
<h6>{product.title}</h6>
|
||||
<h5>{product.price}</h5>
|
||||
<button className='btn'>Add to cart</button>
|
||||
</div>
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Now, what we want is that when we click on the add to cart button the number of cart items should increase. But cart items are added in our navBar component while the add to cart button is added in our Products component.
|
||||
|
||||
**[Ask the learners]**
|
||||
How can I pass data from the product component to the cart component or the navBar component?
|
||||
-> **Redux** is used to pass the data. Redux is flexible as it can be used with other frameworks as well. We can't use prop drilling or context API because of optimisation issues.
|
||||
|
||||
You can install the Redux toolkit using the following command: `npm install @reduxjs/toolkit react-redux`. By installing the redux toolkit two libraries will be installed: **reactjs/toolkit and react-redux**.
|
||||
|
||||
You can also visit [Redux](https://redux-toolkit.js.org/tutorials/quick-start) documentation to read more about the Redux toolkit.
|
||||
|
||||
Before writing redux code install this [chrome](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd/related) extension.
|
||||
|
||||
*When we use redux there are a few things to keep in mind*
|
||||
* Create a centralised store for all your component features and properties.
|
||||
* We can create slices (a slice is for a specific component where every function will be written for that specific component) of different components. Whatever component you are trying to integrate your features for, you should create separate slices for them.
|
||||
|
||||
In the example of the shopping cart, we will create a cart slice. To create a slice there is a method known as `createSlice` which takes an object inside which we can define everything that we want that particular slice to perform.
|
||||
|
||||
`cartSlice.js`
|
||||
|
||||
```javascript!
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
|
||||
const cartSlice = createSlice({
|
||||
name:'cart',
|
||||
initialStat:[],
|
||||
reducers : {
|
||||
add(state, action){
|
||||
state.push(action.payload)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**cartSlice:** It is created for your cart. Everything that you will do for your cart will be added.
|
||||
name
|
||||
**initialState:** The state from where we are going to start there it will be empty.
|
||||
**reducers:** Inside the reducers key you will put all those functions that you want to be implemented in your cart. For example: we want to add to the cart feature and remove from the cart feature inside our redux application.
|
||||
**action.payload:** What is the action you are taking with the data that goes inside action.payload.
|
||||
|
||||
We are pushing action.payload. InitialState was empty but now as we have taken an action all the data will be pushed to the initialState array. After that, you can count the length of all the objects inside the array and whatever will be the length will be the number added to your cart.
|
||||
|
||||
First, we need to export the add reducer that we are destructuring out from the cartSlice.
|
||||
|
||||
Second, you have to export the whole cartSlice.
|
||||
|
||||
```javascript!
|
||||
export const {add} = cartSlice.actions //exporting the add reducer
|
||||
|
||||
export default cartSlice.reducer //exporting the whole cartslice
|
||||
```
|
||||
|
||||
We will be creating a central store named store.js in our Store folder to store everything related to every component will be stored in it.
|
||||
Same way as we have the createSlice method by redux, there's another method known as configureStore.
|
||||
|
||||
As we know we have an InitialState array and to fetch the data from that particular state for that we have a special kind of hook that is provided from react redux known as **useSelector**. This hook has access to all the global states.
|
||||
|
||||
Add useSelector inside the navBar component as shown:
|
||||
|
||||
```javascript!
|
||||
const items = useSelector((state) => state.cart) //use the state of the cart
|
||||
console.log(items.length)
|
||||
```
|
||||
|
||||
the console log will give output 0 as initially, cart items are 0.
|
||||
|
||||
> We have to use Provider from the react-redux library as our app.js will be the initial page that will show all the components. Therefore to wrap all the components we will be using Provider (not from context API) but from react-redux. Inside Provider, we will add a store as we added value inside our context API provider.
|
||||
|
||||
**useDispatch** hook is used to use your reducers. We can dispatch actions to our reducers.
|
||||
|
||||
Whatever product is getting added is your action payload.
|
||||
|
||||
What we want is for the number of cart items should increase as we click on the 'Add to cart' button that exists in the `Products.js` component. To use reducers we will import the useDispatch hook inside our Products.js component.
|
||||
|
||||
|
||||
```javascript!
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { add } from '../Store/cartSlice'
|
||||
|
||||
const dispatch = useDispatch()
|
||||
const handleAdd = (product) => {
|
||||
dispatch(add(product))
|
||||
}
|
||||
|
||||
<button onClick={() => handleAdd(product)} className='btn'>Add to cart</button>
|
||||
```
|
||||
|
||||
After this add `{items.length}` in your Cart Items `<span className='cartCount'>Cart items:{items.length}</span>`
|
||||
|
||||
The number of cart items will increase on the `Add to cart` button.
|
||||
|
||||

|
||||
|
||||
When you open the Redux dev tool in your browser, go to 'State' where you will see the cart inside 'Tree', and 'Chart'.
|
||||
|
||||
Whenever you add an item the data related to that product is added to the cart as you can see in the image.
|
||||
|
||||

|
||||
|
||||
Redux is predictable because you can have control of every step that you are doing. You see the scroll bar in the image shown below, using that scroll bar you can control every step.
|
||||
|
||||

|
||||
|
||||
### Cart page
|
||||
|
||||
The data will be passed the same way we passed the product length in our cart items.
|
||||
|
||||
We want product images, prices, and titles to be displayed on our cart page. And also we will add a remove button to remove any product from our cart.
|
||||
|
||||
Add remove reducer inside our cartSlice in your store.
|
||||
|
||||
`cartSlice.js`
|
||||
|
||||
This reducer will return and filter out from the state that has some products. Wherever the data doesn't match return it and remove the data that matches. Also, export remove reducer the way we did add reducer.
|
||||
|
||||
```javascript=
|
||||
remove(state , action){
|
||||
return state.filter((item)=> item.id !== action.payload)
|
||||
}
|
||||
```
|
||||
|
||||
`Cart.js`
|
||||
|
||||
```javascript=
|
||||
import React from 'react'
|
||||
import {useSelector , useDispatch} from 'react-redux'
|
||||
|
||||
import { remove } from '../Store/cartSlice'
|
||||
|
||||
const Cart = () => {
|
||||
const items = useSelector((state)=> state.cart)
|
||||
const dispatch = useDispatch()
|
||||
|
||||
|
||||
const handleRemove =(itemId)=>{
|
||||
dispatch(remove(itemId))
|
||||
}
|
||||
return (
|
||||
|
||||
<div>
|
||||
<div className='cartWrapper'>
|
||||
|
||||
{
|
||||
items.map((item)=>(
|
||||
<div className='cartCard'>
|
||||
<img src={item.image}></img>
|
||||
<h5>{item.title}</h5>
|
||||
<h5>Price : ${item.price} </h5>
|
||||
|
||||
<button className='remove-btn' onClick={()=> handleRemove(item.id)}>Remove Item</button>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<h1>This is Cart page.</h1>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Cart
|
||||
```
|
Reference in New Issue
Block a user