├── src ├── App.jsx ├── App.css ├── main.jsx ├── components │ ├── Loading.jsx │ ├── MovieCard.jsx │ ├── MovieComponent.jsx │ └── Home.jsx ├── assets │ └── react.svg └── index.css ├── vite.config.js ├── .gitignore ├── index.html ├── package.json └── public └── vite.svg /src/App.jsx: -------------------------------------------------------------------------------- 1 | import Home from "./components/Home"; 2 | 3 | const App = () => { 4 | return ; 5 | }; 6 | 7 | export default App; 8 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .card { 9 | padding: 2em; 10 | } 11 | 12 | .read-the-docs { 13 | color: #888; 14 | } 15 | -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App"; 4 | import "./index.css"; 5 | 6 | ReactDOM.createRoot(document.getElementById("root")).render( 7 | <> 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /src/components/Loading.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Loading = () => { 4 | return ( 5 |
6 |
7 |
8 |
9 |
10 |
11 | ); 12 | }; 13 | 14 | export default Loading; 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/components/MovieCard.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const MovieCard = ({ myData }) => { 4 | const { title, body, id } = myData; 5 | return ( 6 |
7 |
8 |

{id}

9 |

{body.substr(0, 150)}

10 |

{title.substr(0, 15)}

11 |
12 |
13 | ); 14 | }; 15 | 16 | export default MovieCard; 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "infinitescroll", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "axios": "^1.2.2", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.0.26", 18 | "@types/react-dom": "^18.0.9", 19 | "@vitejs/plugin-react": "^3.0.0", 20 | "vite": "^4.0.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/components/MovieComponent.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import MovieCard from "./MovieCard"; 3 | 4 | const MovieComponent = ({ movieInfo }) => { 5 | return ( 6 |
7 |
8 |

List of cards

9 |
10 | {movieInfo.map((curVal, id) => { 11 | return ; 12 | })} 13 |
14 |
15 |
16 | ); 17 | }; 18 | 19 | export default MovieComponent; 20 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Home.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import Loading from "./Loading"; 3 | import MovieComponent from "./MovieComponent"; 4 | 5 | const Home = () => { 6 | const [card, setCard] = useState([]); 7 | const [page, setPage] = useState(1); 8 | const [loading, setLoading] = useState(true); 9 | 10 | const getCardData = async () => { 11 | const res = await fetch( 12 | `https://jsonplaceholder.typicode.com/posts?_limit=9&_page=${page}` 13 | ); 14 | const data = await res.json(); 15 | // console.log(data); 16 | setCard((prev) => [...prev, ...data]); 17 | setLoading(false); 18 | }; 19 | 20 | useEffect(() => { 21 | getCardData(); 22 | }, [page]); 23 | 24 | const handelInfiniteScroll = async () => { 25 | // console.log("scrollHeight" + document.documentElement.scrollHeight); 26 | // console.log("innerHeight" + window.innerHeight); 27 | // console.log("scrollTop" + document.documentElement.scrollTop); 28 | try { 29 | if ( 30 | window.innerHeight + document.documentElement.scrollTop + 1 >= 31 | document.documentElement.scrollHeight 32 | ) { 33 | setLoading(true); 34 | setPage((prev) => prev + 1); 35 | } 36 | } catch (error) { 37 | console.log(error); 38 | } 39 | }; 40 | 41 | useEffect(() => { 42 | window.addEventListener("scroll", handelInfiniteScroll); 43 | return () => window.removeEventListener("scroll", handelInfiniteScroll); 44 | }, []); 45 | 46 | return ( 47 | <> 48 | 49 | {loading && } 50 | 51 | ); 52 | }; 53 | 54 | export default Home; 55 | -------------------------------------------------------------------------------- /src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@300&family=Nunito:ital,wght@0,400;0,700;1,600&family=Work+Sans:wght@300;400;700;900&display=swap"); 2 | 3 | :root { 4 | font-family: Inter, Avenir, Helvetica, Arial, sans-serif; 5 | font-size: 16px; 6 | line-height: 24px; 7 | font-weight: 400; 8 | 9 | color-scheme: light dark; 10 | color: rgba(255, 255, 255, 0.87); 11 | background-color: #242424; 12 | 13 | font-synthesis: none; 14 | text-rendering: optimizeLegibility; 15 | -webkit-font-smoothing: antialiased; 16 | -moz-osx-font-smoothing: grayscale; 17 | -webkit-text-size-adjust: 100%; 18 | } 19 | 20 | html { 21 | font-size: 62.85%; 22 | font-family: "Cormorant Garamond", serif; 23 | } 24 | 25 | a { 26 | font-weight: 500; 27 | color: #646cff; 28 | text-decoration: inherit; 29 | } 30 | a:hover { 31 | color: #535bf2; 32 | } 33 | 34 | h1 { 35 | font-size: 3.2em; 36 | line-height: 1.1; 37 | font-family: "Cormorant Garamond", serif; 38 | text-transform: uppercase; 39 | text-align: center; 40 | margin: 5rem auto; 41 | } 42 | 43 | button { 44 | border-radius: 8px; 45 | border: 1px solid transparent; 46 | padding: 0.6em 1.2em; 47 | font-size: 1em; 48 | font-weight: 500; 49 | font-family: inherit; 50 | background-color: #1a1a1a; 51 | cursor: pointer; 52 | transition: border-color 0.25s; 53 | } 54 | button:hover { 55 | border-color: #646cff; 56 | } 57 | button:focus, 58 | button:focus-visible { 59 | outline: 4px auto -webkit-focus-ring-color; 60 | } 61 | 62 | .wrapper { 63 | width: 100%; 64 | /* margin: 0 auto; */ 65 | } 66 | .container { 67 | /* background-color: red; */ 68 | max-width: 80%; 69 | margin: 0 auto; 70 | } 71 | 72 | .grid { 73 | display: grid; 74 | gap: 3.2rem; 75 | } 76 | .grid-three-column { 77 | grid-template-columns: repeat(3, 1fr); 78 | } 79 | 80 | .card { 81 | /* background-color: #535bf2; */ 82 | padding: 0 3.2rem; 83 | display: flex; 84 | flex-direction: column; 85 | justify-content: center; 86 | align-items: center; 87 | } 88 | 89 | .card-info { 90 | padding: 1rem 2rem; 91 | border-radius: 1rem; 92 | background-color: #213547; 93 | text-align: center; 94 | } 95 | @media screen and (max-width: 1119px) { 96 | .container { 97 | max-width: 90%; 98 | } 99 | .grid { 100 | gap: 1.6rem; 101 | } 102 | 103 | .card { 104 | background-color: #213547; 105 | } 106 | .card-info { 107 | padding: 0rem; 108 | background-color: #213547; 109 | } 110 | } 111 | 112 | .card-id { 113 | width: 3rem; 114 | height: 3rem; 115 | border-radius: 50%; 116 | color: #000; 117 | display: flex; 118 | justify-content: center; 119 | align-items: center; 120 | background-color: #fff; 121 | font-size: 1.6rem; 122 | } 123 | 124 | p { 125 | text-align: justify; 126 | font-family: "Cormorant Garamond", serif; 127 | font-size: 1.4rem; 128 | line-height: 1.6rem; 129 | } 130 | h2 { 131 | padding: 1rem; 132 | background-color: #0e0f0f; 133 | text-align: left; 134 | font-family: "Cormorant Garamond", serif; 135 | text-transform: capitalize; 136 | } 137 | 138 | figure { 139 | height: 15rem; 140 | display: grid; 141 | place-items: center; 142 | } 143 | img { 144 | max-width: 100%; 145 | height: inherit; 146 | } 147 | 148 | .loading-container { 149 | width: 100%; 150 | text-align: center; 151 | margin: 2rem auto; 152 | } 153 | 154 | .lds-ripple { 155 | display: inline-block; 156 | position: relative; 157 | width: 80px; 158 | height: 80px; 159 | } 160 | .lds-ripple div { 161 | position: absolute; 162 | border: 4px solid rgb(63, 219, 144); 163 | opacity: 1; 164 | border-radius: 50%; 165 | animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite; 166 | } 167 | .lds-ripple div:nth-child(2) { 168 | animation-delay: -0.5s; 169 | } 170 | @keyframes lds-ripple { 171 | 0% { 172 | top: 36px; 173 | left: 36px; 174 | width: 0; 175 | height: 0; 176 | opacity: 0; 177 | } 178 | 4.9% { 179 | top: 36px; 180 | left: 36px; 181 | width: 0; 182 | height: 0; 183 | opacity: 0; 184 | } 185 | 5% { 186 | top: 36px; 187 | left: 36px; 188 | width: 0; 189 | height: 0; 190 | opacity: 1; 191 | } 192 | 100% { 193 | top: 0px; 194 | left: 0px; 195 | width: 72px; 196 | height: 72px; 197 | opacity: 0; 198 | } 199 | } 200 | 201 | @media (prefers-color-scheme: light) { 202 | :root { 203 | color: #213547; 204 | background-color: #ffffff; 205 | } 206 | a:hover { 207 | color: #747bff; 208 | } 209 | button { 210 | background-color: #f9f9f9; 211 | } 212 | } 213 | --------------------------------------------------------------------------------