├── .gitignore ├── day-01 ├── container-presenter-pattern │ ├── .env │ ├── postcss.config.js │ ├── vite.config.js │ ├── src │ │ ├── main.jsx │ │ ├── with-pattern │ │ │ └── components │ │ │ │ ├── common │ │ │ │ ├── LoadingSpinner.jsx │ │ │ │ └── ErrorMessage.jsx │ │ │ │ ├── post │ │ │ │ └── PostsList.jsx │ │ │ │ └── profile │ │ │ │ ├── ProfileHeader.jsx │ │ │ │ └── UserProfileContainer.jsx │ │ ├── App.jsx │ │ └── index.css │ ├── .gitignore │ ├── index.html │ ├── .eslintrc.cjs │ ├── package.json │ ├── README.md │ └── public │ │ └── vite.svg ├── banner.jpg ├── task.md └── README.md ├── day-00 ├── banner.jpg └── README.md ├── day-02 ├── banner.jpg ├── controlled-uncontrolled-comp-pattern │ ├── postcss.config.js │ ├── vite.config.js │ ├── src │ │ ├── main.jsx │ │ ├── state-ref │ │ │ └── components │ │ │ │ ├── AutoFocusInput.jsx │ │ │ │ ├── Counter.jsx │ │ │ │ └── CounterWithRef.jsx │ │ ├── index.css │ │ ├── App.css │ │ ├── App.jsx │ │ ├── uncontrolled │ │ │ ├── UncontrolledFormNoRef.jsx │ │ │ └── UncontrolledFeedbackForm.jsx │ │ ├── messy │ │ │ └── FeedbackForm.jsx │ │ └── controlled │ │ │ └── ControlledFeedbackForm.jsx │ ├── .gitignore │ ├── index.html │ ├── .eslintrc.cjs │ ├── package.json │ ├── README.md │ └── public │ │ └── vite.svg ├── task.md └── README.md ├── day-03 ├── banner.jpg ├── compound-components-patterns │ ├── postcss.config.js │ ├── vite.config.js │ ├── src │ │ ├── main.jsx │ │ ├── messy │ │ │ └── Modal.jsx │ │ ├── index.css │ │ ├── with-pattern │ │ │ ├── accordion │ │ │ │ ├── Accordion.jsx │ │ │ │ └── AccordionDemo.jsx │ │ │ └── modal │ │ │ │ └── Modal.jsx │ │ ├── App.css │ │ └── App.jsx │ ├── .gitignore │ ├── index.html │ ├── .eslintrc.cjs │ ├── package.json │ ├── README.md │ └── public │ │ └── vite.svg ├── task.md └── README.md ├── day-04 ├── banner.jpg ├── render-props-pattern │ ├── postcss.config.js │ ├── vite.config.js │ ├── src │ │ ├── main.jsx │ │ ├── with-pattern │ │ │ ├── MouseTracker.jsx │ │ │ └── using-children │ │ │ │ └── MouseTrackerWithChildren.jsx │ │ ├── messy │ │ │ ├── CarTracker.jsx │ │ │ └── BikeTracker.jsx │ │ ├── index.css │ │ ├── App.css │ │ └── App.jsx │ ├── .gitignore │ ├── index.html │ ├── .eslintrc.cjs │ ├── package.json │ ├── README.md │ └── public │ │ └── vite.svg ├── task.md └── README.md ├── day-05 ├── banner.jpg ├── hoc-pattern │ ├── postcss.config.js │ ├── vite.config.js │ ├── src │ │ ├── App.jsx │ │ ├── main.jsx │ │ ├── movies │ │ │ ├── MovieAnalytics.jsx │ │ │ ├── MovieList.jsx │ │ │ ├── MovieWithHOC.jsx │ │ │ └── hoc │ │ │ │ └── withDataFetching.jsx │ │ ├── index.css │ │ └── App.css │ ├── .gitignore │ ├── index.html │ ├── .eslintrc.cjs │ ├── package.json │ ├── README.md │ └── public │ │ └── vite.svg ├── README.md └── task.md ├── day-06 ├── banner.jpg ├── custom-pattern-hooks │ ├── postcss.config.js │ ├── src │ │ ├── actions │ │ │ └── index.js │ │ ├── hooks │ │ │ ├── useToggle.js │ │ │ ├── useAuth.js │ │ │ └── useFetch.js │ │ ├── main.jsx │ │ ├── components │ │ │ ├── ThemeSwitcher.jsx │ │ │ ├── MovieList.jsx │ │ │ └── AuthPanel.jsx │ │ ├── App.jsx │ │ ├── index.css │ │ └── App.css │ ├── vite.config.js │ ├── .gitignore │ ├── index.html │ ├── .eslintrc.cjs │ ├── package.json │ ├── README.md │ └── public │ │ └── vite.svg ├── task.md └── README.md ├── day-07 ├── banner.jpg ├── provider-pattern │ ├── postcss.config.js │ ├── src │ │ ├── context │ │ │ └── index.js │ │ ├── hook │ │ │ ├── useBrand.js │ │ │ └── useTheme.js │ │ ├── main.jsx │ │ ├── provider │ │ │ ├── BrandProvider.jsx │ │ │ └── ThemeProvider.jsx │ │ ├── index.css │ │ ├── App.css │ │ └── App.jsx │ ├── vite.config.js │ ├── .gitignore │ ├── index.html │ ├── .eslintrc.cjs │ ├── package.json │ ├── README.md │ └── public │ │ └── vite.svg ├── task.md └── README.md ├── day-08 ├── banner.jpg ├── optimistic-pattern │ ├── postcss.config.js │ ├── vite.config.js │ ├── src │ │ ├── App.jsx │ │ ├── main.jsx │ │ ├── index.css │ │ └── App.css │ ├── .gitignore │ ├── index.html │ ├── .eslintrc.cjs │ ├── package.json │ ├── README.md │ └── public │ │ └── vite.svg ├── task.md └── README.md ├── day-09 ├── banner.jpg ├── state-reducer-pattern │ ├── postcss.config.js │ ├── src │ │ ├── context │ │ │ └── index.js │ │ ├── main.jsx │ │ ├── hooks │ │ │ └── useFormContext.js │ │ ├── provider │ │ │ └── FormProvider.jsx │ │ ├── reducers │ │ │ ├── toggle-reducer.js │ │ │ └── form-reducer.js │ │ ├── index.css │ │ ├── App.jsx │ │ ├── components │ │ │ ├── Toggle.jsx │ │ │ └── FormFields.jsx │ │ └── App.css │ ├── vite.config.js │ ├── .gitignore │ ├── index.html │ ├── .eslintrc.cjs │ ├── package.json │ ├── README.md │ └── public │ │ └── vite.svg ├── task.md └── README.md ├── day-10 ├── banner.jpg ├── pub-sub-pattern │ ├── postcss.config.js │ ├── src │ │ ├── lib │ │ │ ├── broadcast.js │ │ │ ├── listener.js │ │ │ └── eventBus.js │ │ ├── main.jsx │ │ ├── App.jsx │ │ ├── hooks │ │ │ └── useEvent.js │ │ ├── index.css │ │ ├── components │ │ │ ├── subscriber │ │ │ │ └── CartBadge.jsx │ │ │ └── publisher │ │ │ │ └── AddToCartButton.jsx │ │ └── App.css │ ├── vite.config.js │ ├── .gitignore │ ├── index.html │ ├── .eslintrc.cjs │ ├── package.json │ ├── README.md │ └── public │ │ └── vite.svg ├── task.md └── README.md ├── day-12 ├── banner.jpg ├── slot-pattern │ ├── postcss.config.js │ ├── src │ │ ├── named-slot-map │ │ │ ├── Article.jsx │ │ │ ├── SlotsMapLayout.jsx │ │ │ └── SlotsMapLayoutDemo.jsx │ │ ├── main.jsx │ │ ├── default-slot │ │ │ ├── DefaultSlotDemo.jsx │ │ │ └── DefaultSlotCard.jsx │ │ ├── named-slots │ │ │ ├── CardWithSlotDemo.jsx │ │ │ └── CardWithSlots.jsx │ │ ├── App.jsx │ │ ├── compound │ │ │ ├── CardCompoundDemo.jsx │ │ │ └── CardCompound.jsx │ │ ├── index.css │ │ └── App.css │ ├── vite.config.js │ ├── .gitignore │ ├── index.html │ ├── .eslintrc.cjs │ ├── package.json │ ├── README.md │ └── public │ │ └── vite.svg ├── task.md └── README.md └── day-11 ├── banner-1.jpg ├── banner-2.jpg ├── performance-patterns ├── postcss.config.js ├── src │ ├── performance │ │ ├── component-isolation │ │ │ ├── Visitors.jsx │ │ │ ├── Revenue.jsx │ │ │ ├── UserCard.jsx │ │ │ ├── Dashboard.jsx │ │ │ └── IsolatedDashboard.jsx │ │ ├── derived │ │ │ ├── BetterCart.jsx │ │ │ ├── Cart.jsx │ │ │ └── MoreExamples.jsx │ │ ├── memoization │ │ │ ├── use-callback │ │ │ │ ├── Child.jsx │ │ │ │ └── ChildDemo.jsx │ │ │ ├── memo │ │ │ │ ├── ProfileTracker.jsx │ │ │ │ └── MemoizedProfileTracker.jsx │ │ │ └── use-memo │ │ │ │ ├── UsersSortingDemo.jsx │ │ │ │ └── Users.jsx │ │ ├── rerenders │ │ │ ├── RenderTracker.jsx │ │ │ └── RenderTrackerDemo.jsx │ │ ├── lazy-loading │ │ │ ├── Light.jsx │ │ │ ├── NonLazyLoader.jsx │ │ │ ├── LazyLoader.jsx │ │ │ └── Heavy.jsx │ │ ├── debouncing │ │ │ ├── hooks │ │ │ │ └── useDebounce.js │ │ │ └── SearchBox.jsx │ │ ├── virtualization │ │ │ ├── data.js │ │ │ ├── VirtualList.jsx │ │ │ ├── Item.jsx │ │ │ ├── NonVirtualList.jsx │ │ │ └── VirtualizationDemo.jsx │ │ └── throttling │ │ │ ├── ScrollTracker.jsx │ │ │ └── hooks │ │ │ └── useThrottle.js │ ├── main.jsx │ ├── App.jsx │ ├── utils │ │ └── index.js │ ├── index.css │ ├── App.css │ ├── PartOne.jsx │ └── PartTwo.jsx ├── vite.config.js ├── .gitignore ├── index.html ├── .eslintrc.cjs ├── package.json ├── README.md └── public │ └── vite.svg └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode -------------------------------------------------------------------------------- /day-01/container-presenter-pattern/.env: -------------------------------------------------------------------------------- 1 | VITE_API_BASE_URL=http://localhost:3001 -------------------------------------------------------------------------------- /day-00/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapascript/15-days-of-react-design-patterns/HEAD/day-00/banner.jpg -------------------------------------------------------------------------------- /day-01/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapascript/15-days-of-react-design-patterns/HEAD/day-01/banner.jpg -------------------------------------------------------------------------------- /day-02/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapascript/15-days-of-react-design-patterns/HEAD/day-02/banner.jpg -------------------------------------------------------------------------------- /day-03/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapascript/15-days-of-react-design-patterns/HEAD/day-03/banner.jpg -------------------------------------------------------------------------------- /day-04/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapascript/15-days-of-react-design-patterns/HEAD/day-04/banner.jpg -------------------------------------------------------------------------------- /day-05/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapascript/15-days-of-react-design-patterns/HEAD/day-05/banner.jpg -------------------------------------------------------------------------------- /day-06/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapascript/15-days-of-react-design-patterns/HEAD/day-06/banner.jpg -------------------------------------------------------------------------------- /day-07/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapascript/15-days-of-react-design-patterns/HEAD/day-07/banner.jpg -------------------------------------------------------------------------------- /day-08/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapascript/15-days-of-react-design-patterns/HEAD/day-08/banner.jpg -------------------------------------------------------------------------------- /day-09/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapascript/15-days-of-react-design-patterns/HEAD/day-09/banner.jpg -------------------------------------------------------------------------------- /day-10/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapascript/15-days-of-react-design-patterns/HEAD/day-10/banner.jpg -------------------------------------------------------------------------------- /day-12/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapascript/15-days-of-react-design-patterns/HEAD/day-12/banner.jpg -------------------------------------------------------------------------------- /day-11/banner-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapascript/15-days-of-react-design-patterns/HEAD/day-11/banner-1.jpg -------------------------------------------------------------------------------- /day-11/banner-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapascript/15-days-of-react-design-patterns/HEAD/day-11/banner-2.jpg -------------------------------------------------------------------------------- /day-05/hoc-pattern/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /day-12/slot-pattern/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /day-07/provider-pattern/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /day-08/optimistic-pattern/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /day-10/pub-sub-pattern/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /day-10/pub-sub-pattern/src/lib/broadcast.js: -------------------------------------------------------------------------------- 1 | // broadcast.js 2 | export const crossTabChannel = new BroadcastChannel("app-events"); -------------------------------------------------------------------------------- /day-04/render-props-pattern/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /day-06/custom-pattern-hooks/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /day-09/state-reducer-pattern/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /day-11/performance-patterns/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /day-01/container-presenter-pattern/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /day-03/compound-components-patterns/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /day-02/controlled-uncontrolled-comp-pattern/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /day-12/slot-pattern/src/named-slot-map/Article.jsx: -------------------------------------------------------------------------------- 1 | const Article = () => { 2 | return
Content
; 3 | }; 4 | 5 | export default Article; 6 | -------------------------------------------------------------------------------- /day-09/state-reducer-pattern/src/context/index.js: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | const FormContext = createContext(null); 3 | 4 | export { FormContext }; 5 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/component-isolation/Visitors.jsx: -------------------------------------------------------------------------------- 1 | const Visitors = () => { 2 | return
Visitors
; 3 | }; 4 | 5 | export default Visitors; 6 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/component-isolation/Revenue.jsx: -------------------------------------------------------------------------------- 1 | const Revenue = ({ stats }) => { 2 | return
{stats}
; 3 | }; 4 | 5 | export default Revenue; 6 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/component-isolation/UserCard.jsx: -------------------------------------------------------------------------------- 1 | const UserCard = ({ user }) => { 2 | return
{user}
; 3 | }; 4 | 5 | export default UserCard; 6 | -------------------------------------------------------------------------------- /day-06/custom-pattern-hooks/src/actions/index.js: -------------------------------------------------------------------------------- 1 | export function doLogin(userName, password) { 2 | if (userName && password) { 3 | return true; 4 | } 5 | return false; 6 | } -------------------------------------------------------------------------------- /day-04/task.md: -------------------------------------------------------------------------------- 1 | # Day 04 - Your Task 2 | 3 | Build a Toggle component using the Render Props pattern. 4 | It should manage an internal isOpen state and let the parent decide how to render open/close UI. 5 | -------------------------------------------------------------------------------- /day-05/hoc-pattern/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 | -------------------------------------------------------------------------------- /day-07/provider-pattern/src/context/index.js: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | 3 | const ThemeContext = createContext(); 4 | const BrandContext = createContext(); 5 | 6 | export { ThemeContext, BrandContext }; 7 | -------------------------------------------------------------------------------- /day-10/pub-sub-pattern/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 | -------------------------------------------------------------------------------- /day-12/slot-pattern/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 | -------------------------------------------------------------------------------- /day-04/render-props-pattern/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 | -------------------------------------------------------------------------------- /day-06/custom-pattern-hooks/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 | -------------------------------------------------------------------------------- /day-07/provider-pattern/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 | -------------------------------------------------------------------------------- /day-08/optimistic-pattern/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 | -------------------------------------------------------------------------------- /day-09/state-reducer-pattern/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 | -------------------------------------------------------------------------------- /day-01/container-presenter-pattern/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 | -------------------------------------------------------------------------------- /day-03/compound-components-patterns/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 | -------------------------------------------------------------------------------- /day-02/controlled-uncontrolled-comp-pattern/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 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/derived/BetterCart.jsx: -------------------------------------------------------------------------------- 1 | function BetterCart({ items }) { 2 | const total = items.reduce((acc, item) => acc + item.price, 0); 3 | 4 | return

Total: {total}

; 5 | } 6 | 7 | export default BetterCart; 8 | -------------------------------------------------------------------------------- /day-07/provider-pattern/src/hook/useBrand.js: -------------------------------------------------------------------------------- 1 | import { use } from "react"; 2 | import { BrandContext } from "../context"; 3 | 4 | const useBrand = () => { 5 | const brand = use(BrandContext); 6 | return brand; 7 | }; 8 | 9 | export { useBrand }; 10 | -------------------------------------------------------------------------------- /day-05/hoc-pattern/src/App.jsx: -------------------------------------------------------------------------------- 1 | import MovieWithHOC from "./movies/MovieWithHOC"; 2 | 3 | function App() { 4 | return ( 5 |
6 | 7 |
8 | ); 9 | } 10 | 11 | export default App; 12 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/memoization/use-callback/Child.jsx: -------------------------------------------------------------------------------- 1 | import { memo } from "react"; 2 | const Child = memo(function Child({ onClick }) { 3 | console.log("Child Rendered"); 4 | return ; 5 | }); 6 | 7 | export default Child; 8 | -------------------------------------------------------------------------------- /day-08/optimistic-pattern/src/App.jsx: -------------------------------------------------------------------------------- 1 | import LikeButton from "./components/LikeButton"; 2 | function App() { 3 | return ( 4 |
5 | 6 |
7 | ); 8 | } 9 | 10 | export default App; 11 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/memoization/memo/ProfileTracker.jsx: -------------------------------------------------------------------------------- 1 | import { memo } from "react"; 2 | 3 | const MemoizedCard = memo(function ProfileCard({ name }) { 4 | console.log("Rendered "); 5 | return

{name}

; 6 | }); 7 | 8 | export default MemoizedCard; 9 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/rerenders/RenderTracker.jsx: -------------------------------------------------------------------------------- 1 | import {useRef} from "react"; 2 | 3 | function RenderTracker() { 4 | const renders = useRef(0); 5 | renders.current++; 6 | 7 | return

Rendered: {renders.current} times

; 8 | } 9 | 10 | export default RenderTracker -------------------------------------------------------------------------------- /day-05/hoc-pattern/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /day-12/slot-pattern/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /day-06/custom-pattern-hooks/src/hooks/useToggle.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | function useToggle(initial = false) { 4 | const [value, setValue] = useState(initial); 5 | const toggle = () => setValue((prev) => !prev); 6 | return [value, toggle]; 7 | } 8 | 9 | export { useToggle } 10 | -------------------------------------------------------------------------------- /day-07/provider-pattern/src/hook/useTheme.js: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import { ThemeContext } from "../context"; 3 | 4 | const useTheme = () => { 5 | const { theme, toggleTheme } = useContext(ThemeContext); 6 | 7 | return { theme, toggleTheme }; 8 | }; 9 | 10 | export { useTheme }; 11 | -------------------------------------------------------------------------------- /day-04/render-props-pattern/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /day-06/custom-pattern-hooks/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /day-08/optimistic-pattern/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /day-09/state-reducer-pattern/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /day-01/container-presenter-pattern/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /day-03/compound-components-patterns/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /day-02/controlled-uncontrolled-comp-pattern/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /day-11/performance-patterns/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 | babel: { 8 | plugins: ['babel-plugin-react-compiler'], 9 | }, 10 | })], 11 | }) 12 | -------------------------------------------------------------------------------- /day-01/container-presenter-pattern/src/with-pattern/components/common/LoadingSpinner.jsx: -------------------------------------------------------------------------------- 1 | 2 | 3 | const LoadingSpinner = ({message}) => { 4 | return ( 5 |
6 |
7 |

{message}

8 |
9 | ) 10 | } 11 | 12 | export default LoadingSpinner -------------------------------------------------------------------------------- /day-09/state-reducer-pattern/src/hooks/useFormContext.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { FormContext } from "../context"; 3 | 4 | export function useFormContext() { 5 | const context = useContext(FormContext); 6 | if (!context) 7 | throw new Error("useFormContext must be used within a FormProvider"); 8 | return context; 9 | } -------------------------------------------------------------------------------- /day-10/task.md: -------------------------------------------------------------------------------- 1 | # Day 10 - Your Task 2 | 3 | ## Build a complete Notifications System using Pub-Sub 4 | 5 | - eventBus.js, useEvent.js 6 | - Publisher: buttons to emit notifications 7 | - Subscriber: Notification panel 8 | - Add categories like success, error, warning 9 | - Add dismiss / auto-hide 10 | - Store notifications in localStorage 11 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/App.jsx: -------------------------------------------------------------------------------- 1 | import PartOne from "./PartOne"; 2 | import PartTwo from "./PartTwo"; 3 | function App() { 4 | return ( 5 |
6 | 7 | 8 |
9 | ); 10 | } 11 | 12 | export default App; 13 | -------------------------------------------------------------------------------- /day-03/task.md: -------------------------------------------------------------------------------- 1 | # Day 03 - Your Tasks 2 | 3 | - Build a simple Card component using this pattern. Your Card should have , , and . 4 | 5 | - Make a . Try using it in a few different ways to see how clean the code looks compared to a giant Card with 10 props.” 6 | 7 | - Create a Tab component using this pattern. 8 | -------------------------------------------------------------------------------- /day-12/slot-pattern/src/default-slot/DefaultSlotDemo.jsx: -------------------------------------------------------------------------------- 1 | import DefaultSlotCard from "./DefaultSlotCard"; 2 | 3 | const DefaultSlotDemo = () => { 4 | return ( 5 | 6 |

Title

7 |

Body goes here.

8 |
9 | ); 10 | }; 11 | 12 | export default DefaultSlotDemo; 13 | -------------------------------------------------------------------------------- /day-02/task.md: -------------------------------------------------------------------------------- 1 | # Day 02 - Your Tasks 2 | 3 | Build a simple Contact Form 4 | 5 | - Implement as a controlled component 6 | - Implement as an uncontrolled component(With and Without Ref) 7 | 8 | Create a Comparison Table: 9 | 10 | - Compare pros/cons: code styles, validation, reset, readability, complexity. 11 | - Create an MD file and capture the findings. 12 | -------------------------------------------------------------------------------- /day-10/pub-sub-pattern/src/lib/listener.js: -------------------------------------------------------------------------------- 1 | import { crossTabChannel } from "./broadcast"; 2 | import { eventBus } from "./eventBus"; 3 | 4 | crossTabChannel.onmessage = ({ data }) => { 5 | const { eventName, payload } = data; 6 | 7 | // Re-publish locally WITHOUT rebroadcasting 8 | eventBus.publish(eventName, payload, { broadcast: false }); 9 | }; 10 | -------------------------------------------------------------------------------- /day-06/custom-pattern-hooks/src/components/ThemeSwitcher.jsx: -------------------------------------------------------------------------------- 1 | import { useToggle } from "../hooks/useToggle"; 2 | 3 | function ThemeSwitcher() { 4 | const [isDark, toggleTheme] = useToggle(); 5 | return ( 6 | 9 | ); 10 | } 11 | 12 | export default ThemeSwitcher; -------------------------------------------------------------------------------- /day-01/container-presenter-pattern/src/with-pattern/components/common/ErrorMessage.jsx: -------------------------------------------------------------------------------- 1 | const ErrorMessage = ({ title, message, onRetry }) => { 2 | return ( 3 |
4 |

{title}

5 |

{message}

6 | 7 |
8 | ); 9 | }; 10 | 11 | export default ErrorMessage; 12 | -------------------------------------------------------------------------------- /day-01/container-presenter-pattern/src/App.jsx: -------------------------------------------------------------------------------- 1 | // import UserProfile from "./messy-way/components/UserProfile"; 2 | 3 | import "./App.css"; 4 | import UserProfileContainer from "./with-pattern/components/profile/UserProfileContainer"; 5 | function App() { 6 | return ( 7 |
8 | 9 |
10 | ); 11 | } 12 | 13 | export default App; 14 | -------------------------------------------------------------------------------- /day-07/provider-pattern/.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 | -------------------------------------------------------------------------------- /day-10/pub-sub-pattern/.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 | -------------------------------------------------------------------------------- /day-10/pub-sub-pattern/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | // ⬅️ Import the listener (runs immediately) 6 | import "./lib/listener.js"; 7 | 8 | ReactDOM.createRoot(document.getElementById('root')).render( 9 | 10 | 11 | , 12 | ) 13 | -------------------------------------------------------------------------------- /day-12/slot-pattern/.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 | -------------------------------------------------------------------------------- /day-04/render-props-pattern/.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 | -------------------------------------------------------------------------------- /day-05/hoc-pattern/.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 | .env 26 | -------------------------------------------------------------------------------- /day-06/custom-pattern-hooks/.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 | -------------------------------------------------------------------------------- /day-08/optimistic-pattern/.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 | -------------------------------------------------------------------------------- /day-09/state-reducer-pattern/.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 | -------------------------------------------------------------------------------- /day-11/performance-patterns/.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 | -------------------------------------------------------------------------------- /day-01/container-presenter-pattern/.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 | -------------------------------------------------------------------------------- /day-03/compound-components-patterns/.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 | -------------------------------------------------------------------------------- /day-10/pub-sub-pattern/src/App.jsx: -------------------------------------------------------------------------------- 1 | import AddToCartButton from "./components/publisher/AddToCartButton"; 2 | import CartBadge from "./components/subscriber/CartBadge"; 3 | 4 | function App() { 5 | return ( 6 |
7 | 8 | 9 |
10 | ); 11 | } 12 | 13 | export default App; 14 | -------------------------------------------------------------------------------- /day-02/controlled-uncontrolled-comp-pattern/.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 | -------------------------------------------------------------------------------- /day-12/slot-pattern/src/named-slots/CardWithSlotDemo.jsx: -------------------------------------------------------------------------------- 1 | import CardWithSlots from "./CardWithSlots"; 2 | 3 | const CardWithSlotDemo = () => { 4 | return ( 5 | User Profile} 7 | content={

Biography and details...

} 8 | footer={} 9 | /> 10 | ); 11 | }; 12 | 13 | export default CardWithSlotDemo; 14 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/lazy-loading/Light.jsx: -------------------------------------------------------------------------------- 1 | export default function Light() { 2 | console.log("[Light] rendered"); 3 | return ( 4 |
5 |

Light Component

6 |

7 | This component is small and always included in the initial 8 | bundle. 9 |

10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/derived/Cart.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | 3 | const Cart = ({ items }) => { 4 | const [total, setTotal] = useState(0); 5 | 6 | useEffect(() => { 7 | const sum = items.reduce((acc, item) => acc + item.price, 0); 8 | setTotal(sum); 9 | }, [items]); 10 | 11 | return

Total: {total}

; 12 | }; 13 | 14 | export default Cart; 15 | -------------------------------------------------------------------------------- /day-06/custom-pattern-hooks/src/App.jsx: -------------------------------------------------------------------------------- 1 | import ThemeSwitcher from "./components/ThemeSwitcher"; 2 | import MoviesList from "./components/MovieList"; 3 | import AuthPanel from "./components/AuthPanel"; 4 | 5 | function App() { 6 | return ( 7 |
8 | 9 | 10 | 11 |
12 | ); 13 | } 14 | 15 | export default App; 16 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/utils/index.js: -------------------------------------------------------------------------------- 1 | function getUsers() { 2 | const usernames = []; 3 | const count = 1000; 4 | 5 | for (let i = 0; i < count; i++) { 6 | const firstName = "Tapas"; 7 | const lastName = "Adhikary"; 8 | 9 | const username = `${firstName}${lastName}`; 10 | usernames.push(username); 11 | } 12 | 13 | return usernames; 14 | } 15 | 16 | export { getUsers }; 17 | -------------------------------------------------------------------------------- /day-05/hoc-pattern/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /day-12/slot-pattern/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /day-07/provider-pattern/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /day-08/optimistic-pattern/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /day-10/pub-sub-pattern/src/hooks/useEvent.js: -------------------------------------------------------------------------------- 1 | // useEvent.js 2 | import { useEffect } from "react"; 3 | import { eventBus } from "../lib/eventBus"; 4 | 5 | export function useEvent(eventName, handler) { 6 | useEffect(() => { 7 | // subscribe returns the unsubscribe function 8 | const unsubscribe = eventBus.subscribe(eventName, handler); 9 | 10 | return () => unsubscribe(); // cleanup on unmount 11 | }, [eventName, handler]); 12 | } -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/component-isolation/Dashboard.jsx: -------------------------------------------------------------------------------- 1 | import Revenue from "./Revenue"; 2 | import UserCard from "./UserCard"; 3 | import Visitors from "./Visitors"; 4 | function Dashboard({ user, stats }) { 5 | return ( 6 | <> 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | 14 | export default Dashboard; 15 | -------------------------------------------------------------------------------- /day-04/render-props-pattern/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /day-06/custom-pattern-hooks/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /day-09/state-reducer-pattern/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /day-11/performance-patterns/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /day-01/container-presenter-pattern/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /day-07/task.md: -------------------------------------------------------------------------------- 1 | # Day 07 - Your Task 2 | 3 | ## Build an Auth Context & Provider 4 | 5 | - Should hold user and isAuthenticated 6 | - Provide login() and logout() methods 7 | - Consume it in Navbar and Dashboard components 8 | 9 | ## Build a Language Context 10 | 11 | - Store selected language and toggle it 12 | - Update displayed text dynamically across components 13 | 14 | ## Combine Them 15 | 16 | Bonus points if you can combine them both cleanly! 17 | -------------------------------------------------------------------------------- /day-10/pub-sub-pattern/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Pub-Sub Pattern in React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /day-02/controlled-uncontrolled-comp-pattern/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /day-03/compound-components-patterns/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Compound Components Pattern 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/debouncing/hooks/useDebounce.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | 3 | export function useDebounce(value, delay = 500) { 4 | const [debouncedValue, setDebouncedValue] = useState(value); 5 | 6 | useEffect(() => { 7 | const id = setTimeout(() => { 8 | setDebouncedValue(value); 9 | }, delay); 10 | 11 | return () => clearTimeout(id); 12 | }, [value, delay]); 13 | 14 | return debouncedValue; 15 | } -------------------------------------------------------------------------------- /day-09/task.md: -------------------------------------------------------------------------------- 1 | # Day 09 - Your Task 2 | 3 | ## Build a “Form Wizard” Component using the State Reducer Pattern 4 | 5 | The wizard should have multiple steps (next, prev, reset). 6 | 7 | - Internal reducer manages state transitions. 8 | - Allow parent component to provide a custom reducer that, for example: 9 | - Prevents skipping incomplete steps. 10 | - Auto-saves data on next. 11 | 12 | ## Integrate with a Provider Context 13 | 14 | Create a Progress Bar that multiple components can aceess. 15 | -------------------------------------------------------------------------------- /day-02/controlled-uncontrolled-comp-pattern/src/state-ref/components/AutoFocusInput.jsx: -------------------------------------------------------------------------------- 1 | import { useRef, useEffect } from "react"; 2 | 3 | function AutoFocusInput() { 4 | const inputRef = useRef(null); 5 | 6 | useEffect(() => { 7 | inputRef.current.focus(); // directly access DOM 8 | }, []); 9 | 10 | return ; 14 | } 15 | 16 | export default AutoFocusInput; -------------------------------------------------------------------------------- /day-02/controlled-uncontrolled-comp-pattern/src/state-ref/components/Counter.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | function Counter() { 4 | const [count, setCount] = useState(0); // state variable 5 | 6 | const increment = () => setCount(count + 1); 7 | 8 | return ( 9 |
10 |

Count: {count}

11 | 12 |
13 | ); 14 | } 15 | 16 | export default Counter; -------------------------------------------------------------------------------- /day-12/slot-pattern/src/App.jsx: -------------------------------------------------------------------------------- 1 | import DefaultSlotDemo from "./default-slot/DefaultSlotDemo"; 2 | import SlotsMapLayoutDemo from "./named-slot-map/SlotsMapLayoutDemo"; 3 | import CardWithSlotDemo from "./named-slots/CardWithSlotDemo"; 4 | function App() { 5 | return ( 6 |
7 | 8 | 9 | 10 |
11 | ); 12 | } 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /day-06/custom-pattern-hooks/src/hooks/useAuth.js: -------------------------------------------------------------------------------- 1 | import {useState} from "react"; 2 | import { doLogin } from "../actions"; 3 | 4 | function useAuth() { 5 | const [user, setUser] = useState(null); 6 | 7 | const login = async (username, password) => { 8 | // fake API call 9 | const loggedIn = await doLogin(username, password) 10 | loggedIn && setUser({ name: username }); 11 | }; 12 | 13 | const logout = () => setUser(null); 14 | 15 | return { user, login, logout, isAuthenticated: !!user }; 16 | } 17 | 18 | export {useAuth} -------------------------------------------------------------------------------- /day-12/slot-pattern/src/named-slot-map/SlotsMapLayout.jsx: -------------------------------------------------------------------------------- 1 | export default function SlotsMapLayout({ slots = {} }) { 2 | return ( 3 |
4 | 5 |
6 |
{slots.header}
7 |
{slots.content}
8 |
{slots.footer}
9 |
10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /day-12/slot-pattern/src/named-slot-map/SlotsMapLayoutDemo.jsx: -------------------------------------------------------------------------------- 1 | import Article from "./Article"; 2 | import SlotsMapLayout from "./SlotsMapLayout"; 3 | const SlotsMapLayoutDemo = () => { 4 | return ( 5 | Title, 8 | sidebar: , 9 | content:
, 10 | footer: Foot, 11 | }} 12 | /> 13 | ); 14 | }; 15 | 16 | export default SlotsMapLayoutDemo; 17 | -------------------------------------------------------------------------------- /day-05/hoc-pattern/src/movies/MovieAnalytics.jsx: -------------------------------------------------------------------------------- 1 | function MovieAnalytics({ data }) { 2 | const totalMovies = data.length; 3 | const averageRating = 4 | data.reduce((acc, curr) => acc + curr.rating, 0) / totalMovies; 5 | 6 | return ( 7 |
8 |

📊 Movie Analytics

9 |

Total Movies: {totalMovies}

10 |

Average Rating: {averageRating.toFixed(1)}

11 |
12 | ); 13 | } 14 | 15 | export default MovieAnalytics 16 | -------------------------------------------------------------------------------- /day-04/render-props-pattern/src/with-pattern/MouseTracker.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | function MouseTracker({ render }) { 3 | const [pos, setPos] = useState({ x: 0, y: 0 }); 4 | 5 | function handleMouseMove(e) { 6 | setPos({ x: e.clientX, y: e.clientY }); 7 | } 8 | 9 | return ( 10 |
14 | {render(pos)} 15 |
16 | ); 17 | } 18 | 19 | export default MouseTracker; 20 | -------------------------------------------------------------------------------- /day-05/hoc-pattern/src/movies/MovieList.jsx: -------------------------------------------------------------------------------- 1 | function MovieList({ data }) { 2 | return ( 3 |
4 |

🎞️ Movie List

5 |
    6 | {data.map((movie) => ( 7 |
  • 8 | {movie.title} ({movie.year}) 9 |
  • 10 | ))} 11 |
12 |
13 | ); 14 | } 15 | 16 | export default MovieList; 17 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/rerenders/RenderTrackerDemo.jsx: -------------------------------------------------------------------------------- 1 | import {useState} from 'react' 2 | import RenderTracker from './RenderTracker'; 3 | 4 | const RenderTrackerDemo = () => { 5 | const [value, setValue] = useState(""); 6 | 7 | return ( 8 |
9 | 10 | setValue(e.target.value)} 14 | /> 15 |
16 | ); 17 | } 18 | 19 | export default RenderTrackerDemo -------------------------------------------------------------------------------- /day-07/provider-pattern/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.jsx"; 4 | import "./index.css"; 5 | import BrandProvider from "./provider/BrandProvider.jsx"; 6 | import ThemeProvider from "./provider/ThemeProvider.jsx"; 7 | ReactDOM.createRoot(document.getElementById("root")).render( 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | ); 16 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/virtualization/data.js: -------------------------------------------------------------------------------- 1 | import { useMemo } from "react"; 2 | 3 | export function useFakeUsers(count = 50000) { 4 | return useMemo(() => { 5 | const users = new Array(count); 6 | for (let i = 0; i < count; i++) { 7 | users[i] = { 8 | id: i, 9 | name: `User ${i}`, 10 | email: `user${i}@example.com`, 11 | bio: `Short bio for user ${i} — generated for demo.`, 12 | }; 13 | } 14 | return users; 15 | }, [count]); 16 | } 17 | -------------------------------------------------------------------------------- /day-09/state-reducer-pattern/src/provider/FormProvider.jsx: -------------------------------------------------------------------------------- 1 | import { useReducer } from "react"; 2 | import { FormContext } from "../context"; 3 | import { defaultFormReducer } from "../reducers/form-reducer"; 4 | 5 | export function FormProvider({ reducer = defaultFormReducer, children }) { 6 | const [state, dispatch] = useReducer(reducer, { 7 | values: { name: "", email: "" }, 8 | errors: {}, 9 | }); 10 | 11 | const value = { state, dispatch }; 12 | return ( 13 | {children} 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/component-isolation/IsolatedDashboard.jsx: -------------------------------------------------------------------------------- 1 | import {memo} from "react"; 2 | 3 | import Revenue from "./Revenue"; 4 | import UserCard from "./UserCard"; 5 | import Visitors from "./Visitors"; 6 | 7 | const MUserCard = memo(UserCard); 8 | const MRevenue = memo(Revenue); 9 | 10 | const IsolatedDashboard = ({ user, stats }) => { 11 | return ( 12 | <> 13 | 14 | 15 | 16 | 17 | ); 18 | }; 19 | 20 | export default IsolatedDashboard; 21 | -------------------------------------------------------------------------------- /day-03/compound-components-patterns/src/messy/Modal.jsx: -------------------------------------------------------------------------------- 1 | function Modal({ title, body, primaryAction, secondaryAction }) { 2 | return ( 3 |
4 |
5 |

{title}

6 |

{body}

7 |
8 | {secondaryAction} 9 | {primaryAction} 10 |
11 |
12 |
13 | ); 14 | } 15 | 16 | export default Modal; 17 | -------------------------------------------------------------------------------- /day-04/render-props-pattern/src/messy/CarTracker.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | const CarTracker = () => { 4 | const [pos, setPos] = useState({ x: 0, y: 0 }); 5 | 6 | function handleMouseMove(e) { 7 | setPos({ x: e.clientX, y: e.clientY }); 8 | } 9 | return ( 10 |
14 |

15 | 🚗 Car is at ({pos.x}, {pos.y}) 16 |

17 |
18 | ); 19 | }; 20 | 21 | export default CarTracker; 22 | -------------------------------------------------------------------------------- /day-05/hoc-pattern/src/movies/MovieWithHOC.jsx: -------------------------------------------------------------------------------- 1 | 2 | import { withDataFetching } from "./hoc/withDataFetching"; 3 | import MovieList from "./MovieList"; 4 | import MovieAnalytics from "./MovieAnalytics"; 5 | 6 | 7 | 8 | // Wrap both components with the same HOC 9 | const MovieListWithData = withDataFetching(MovieList); 10 | const MovieAnalyticsWithData = withDataFetching(MovieAnalytics); 11 | 12 | export default function MovieWithHOC() { 13 | return ( 14 |
15 | 16 | 17 |
18 | ); 19 | } -------------------------------------------------------------------------------- /day-04/render-props-pattern/src/messy/BikeTracker.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | const BikeTracker = () => { 4 | const [pos, setPos] = useState({ x: 0, y: 0 }); 5 | 6 | function handleMouseMove(e) { 7 | setPos({ x: e.clientX, y: e.clientY }); 8 | } 9 | 10 | return ( 11 |
15 |

16 | 🏍️ Bike is at ({pos.x}, {pos.y}) 17 |

18 |
19 | ); 20 | }; 21 | 22 | export default BikeTracker; 23 | -------------------------------------------------------------------------------- /day-05/hoc-pattern/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | 3 | /* 4 | The default border color has changed to `currentColor` in Tailwind CSS v4, 5 | so we've added these compatibility styles to make sure everything still 6 | looks the same as it did with Tailwind CSS v3. 7 | 8 | If we ever want to remove these styles, we need to add an explicit border 9 | color utility to any element that depends on these defaults. 10 | */ 11 | @layer base { 12 | *, 13 | ::after, 14 | ::before, 15 | ::backdrop, 16 | ::file-selector-button { 17 | border-color: var(--color-gray-200, currentColor); 18 | } 19 | } -------------------------------------------------------------------------------- /day-04/render-props-pattern/src/with-pattern/using-children/MouseTrackerWithChildren.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | function MouseTrackerWithChildren({ children }) { 4 | const [pos, setPos] = useState({ x: 0, y: 0 }); 5 | 6 | function handleMouseMove(e) { 7 | setPos({ x: e.clientX, y: e.clientY }); 8 | } 9 | 10 | return ( 11 |
15 | {children(pos)} 16 |
17 | ); 18 | } 19 | 20 | export default MouseTrackerWithChildren; 21 | -------------------------------------------------------------------------------- /day-12/slot-pattern/src/compound/CardCompoundDemo.jsx: -------------------------------------------------------------------------------- 1 | import CardCompound from "./CardCompound"; 2 | 3 | const CardCompoundDemo = () => { 4 | return ( 5 | 6 | 7 |

Title

8 |
9 | 10 |

Content...

11 |
12 | 13 | 14 | 15 |
16 | ); 17 | }; 18 | 19 | export default CardCompoundDemo; 20 | -------------------------------------------------------------------------------- /day-04/render-props-pattern/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | 3 | /* 4 | The default border color has changed to `currentColor` in Tailwind CSS v4, 5 | so we've added these compatibility styles to make sure everything still 6 | looks the same as it did with Tailwind CSS v3. 7 | 8 | If we ever want to remove these styles, we need to add an explicit border 9 | color utility to any element that depends on these defaults. 10 | */ 11 | @layer base { 12 | *, 13 | ::after, 14 | ::before, 15 | ::backdrop, 16 | ::file-selector-button { 17 | border-color: var(--color-gray-200, currentColor); 18 | } 19 | } -------------------------------------------------------------------------------- /day-08/task.md: -------------------------------------------------------------------------------- 1 | # Day 08 - Your Task 2 | 3 | “Alright, your task for today 4 | 5 | ## Build an optimistic comment feature for a mini blog 6 | 7 | 1. A Post view with existing comments pulled from GET /api/posts/:id/comments. (Use fake-server) 8 | 9 | 2. A comment form. When user submits: 10 | 11 | - Immediately show the comment in the list with a “Sending…” label using useOptimistic(). 12 | - Send the comment to the server asynchronously. 13 | - On success, replace the optimistic entry with the server-sent item (with real id and createdAt). 14 | - On failure, remove the optimistic comment and show a toast/error message. 15 | -------------------------------------------------------------------------------- /day-01/container-presenter-pattern/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | 3 | /* 4 | The default border color has changed to `currentColor` in Tailwind CSS v4, 5 | so we've added these compatibility styles to make sure everything still 6 | looks the same as it did with Tailwind CSS v3. 7 | 8 | If we ever want to remove these styles, we need to add an explicit border 9 | color utility to any element that depends on these defaults. 10 | */ 11 | @layer base { 12 | *, 13 | ::after, 14 | ::before, 15 | ::backdrop, 16 | ::file-selector-button { 17 | border-color: var(--color-gray-200, currentColor); 18 | } 19 | } -------------------------------------------------------------------------------- /day-03/compound-components-patterns/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | 3 | /* 4 | The default border color has changed to `currentColor` in Tailwind CSS v4, 5 | so we've added these compatibility styles to make sure everything still 6 | looks the same as it did with Tailwind CSS v3. 7 | 8 | If we ever want to remove these styles, we need to add an explicit border 9 | color utility to any element that depends on these defaults. 10 | */ 11 | @layer base { 12 | *, 13 | ::after, 14 | ::before, 15 | ::backdrop, 16 | ::file-selector-button { 17 | border-color: var(--color-gray-200, currentColor); 18 | } 19 | } -------------------------------------------------------------------------------- /day-09/state-reducer-pattern/src/reducers/toggle-reducer.js: -------------------------------------------------------------------------------- 1 | function toggleReducer(state, action) { 2 | switch (action.type) { 3 | case "toggle": 4 | return { on: !state.on, clicks: state.clicks++ }; 5 | default: 6 | return state; 7 | } 8 | } 9 | 10 | function customToggleReducer(state, action) { 11 | switch (action.type) { 12 | case "toggle": 13 | if (state.clicks >= 3) return state; 14 | return { on: !state.on, clicks: state.clicks + 1 }; 15 | default: 16 | return state; 17 | } 18 | } 19 | 20 | export { toggleReducer, customToggleReducer }; 21 | -------------------------------------------------------------------------------- /day-02/controlled-uncontrolled-comp-pattern/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | 3 | /* 4 | The default border color has changed to `currentColor` in Tailwind CSS v4, 5 | so we've added these compatibility styles to make sure everything still 6 | looks the same as it did with Tailwind CSS v3. 7 | 8 | If we ever want to remove these styles, we need to add an explicit border 9 | color utility to any element that depends on these defaults. 10 | */ 11 | @layer base { 12 | *, 13 | ::after, 14 | ::before, 15 | ::backdrop, 16 | ::file-selector-button { 17 | border-color: var(--color-gray-200, currentColor); 18 | } 19 | } -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/memoization/use-memo/UsersSortingDemo.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { getUsers } from "../../../utils"; 3 | import Users from "./Users"; 4 | 5 | const UsersSortingDemo = () => { 6 | const [count, setCount] = useState(0); 7 | const [users] = useState(() => getUsers()); // assume it returns 10,000 users 8 | 9 | return ( 10 | <> 11 |

{count}

12 | 13 | 14 | 15 | ); 16 | }; 17 | 18 | export default UsersSortingDemo; 19 | -------------------------------------------------------------------------------- /day-01/task.md: -------------------------------------------------------------------------------- 1 | # Day 01 - Your Tasks 2 | 3 | Create a `ProductListContainer` that handles: 4 | 5 | - API calls and data fetching 6 | - Cart management 7 | - Error handling 8 | 9 | Create a `ProductListPresenter` that handles: 10 | 11 | - Rendering products 12 | - Sort/filter UI interactions 13 | - Add to cart button clicks 14 | 15 | Break down into smaller presenters: 16 | 17 | - ProductCard 18 | - SortFilterControls 19 | - CartSummary 20 | 21 | ## Success Criteria 22 | 23 | - Container has no JSX rendering logic 24 | - Presenter has no API calls or business logic 25 | - Components are reusable and testable 26 | - Clear separation of concerns 27 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/memoization/memo/MemoizedProfileTracker.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import MemoizedCard from "./ProfileTracker"; 3 | 4 | const MemoizedProfileTracker = () => { 5 | const [value, setValue] = useState(""); 6 | 7 | return ( 8 |
9 | setValue(e.target.value)} 13 | /> 14 | 15 |
16 | ); 17 | }; 18 | 19 | export default MemoizedProfileTracker; 20 | -------------------------------------------------------------------------------- /day-07/provider-pattern/src/provider/BrandProvider.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | import { BrandContext } from "../context"; 3 | import PropTypes from 'prop-types'; 4 | 5 | const BrandProvider = ({ children }) => { 6 | const [brand, setBrand] = useState(null); 7 | 8 | useEffect(() => { 9 | // FAKE an API Call 10 | const data = {"name": "tapaScript", "color": "#765G45"} 11 | setBrand(data); 12 | }, []); 13 | 14 | return ( 15 | 16 | {children} 17 | 18 | ); 19 | }; 20 | BrandProvider.propTypes = { 21 | children: PropTypes.node.isRequired, 22 | }; 23 | 24 | export default BrandProvider; -------------------------------------------------------------------------------- /day-07/provider-pattern/src/provider/ThemeProvider.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import { ThemeContext } from '../context'; 5 | 6 | const ThemeProvider = ({ children }) => { 7 | const [theme, setTheme] = useState(false); 8 | 9 | const toggleTheme = () => { 10 | setTheme((prev) => !prev); 11 | document.body.classList.toggle("dark"); 12 | }; 13 | 14 | return ( 15 | 16 | {children} 17 | 18 | ); 19 | }; 20 | ThemeProvider.propTypes = { 21 | children: PropTypes.node.isRequired, 22 | }; 23 | 24 | export default ThemeProvider; -------------------------------------------------------------------------------- /day-06/custom-pattern-hooks/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react/jsx-no-target-blank': 'off', 16 | 'react-refresh/only-export-components': [ 17 | 'warn', 18 | { allowConstantExport: true }, 19 | ], 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /day-07/provider-pattern/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react/jsx-no-target-blank': 'off', 16 | 'react-refresh/only-export-components': [ 17 | 'warn', 18 | { allowConstantExport: true }, 19 | ], 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /day-12/slot-pattern/src/default-slot/DefaultSlotCard.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple Card that renders whatever is passed as children. 3 | * This is the simplest slot: a single, unnamed slot. 4 | */ 5 | export default function DefaultSlotCard({ children }) { 6 | return ( 7 |
15 | {children} 16 |
17 | ); 18 | } 19 | 20 | /* 21 | Usage: 22 | 23 |

Title

24 |

Body goes here.

25 |
26 | */ 27 | -------------------------------------------------------------------------------- /day-12/slot-pattern/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | 3 | /* 4 | The default border color has changed to `currentColor` in Tailwind CSS v4, 5 | so we've added these compatibility styles to make sure everything still 6 | looks the same as it did with Tailwind CSS v3. 7 | 8 | If we ever want to remove these styles, we need to add an explicit border 9 | color utility to any element that depends on these defaults. 10 | */ 11 | @layer base { 12 | *, 13 | ::after, 14 | ::before, 15 | ::backdrop, 16 | ::file-selector-button { 17 | border-color: var(--color-gray-200, currentColor); 18 | } 19 | } 20 | 21 | body { 22 | background-color: black; 23 | color: #ffffff; 24 | } 25 | -------------------------------------------------------------------------------- /day-06/custom-pattern-hooks/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | 3 | /* 4 | The default border color has changed to `currentColor` in Tailwind CSS v4, 5 | so we've added these compatibility styles to make sure everything still 6 | looks the same as it did with Tailwind CSS v3. 7 | 8 | If we ever want to remove these styles, we need to add an explicit border 9 | color utility to any element that depends on these defaults. 10 | */ 11 | @layer base { 12 | *, 13 | ::after, 14 | ::before, 15 | ::backdrop, 16 | ::file-selector-button { 17 | border-color: var(--color-gray-200, currentColor); 18 | } 19 | } 20 | 21 | body { 22 | background-color: black; 23 | color: #ffffff; 24 | } -------------------------------------------------------------------------------- /day-07/provider-pattern/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | 3 | /* 4 | The default border color has changed to `currentColor` in Tailwind CSS v4, 5 | so we've added these compatibility styles to make sure everything still 6 | looks the same as it did with Tailwind CSS v3. 7 | 8 | If we ever want to remove these styles, we need to add an explicit border 9 | color utility to any element that depends on these defaults. 10 | */ 11 | @layer base { 12 | *, 13 | ::after, 14 | ::before, 15 | ::backdrop, 16 | ::file-selector-button { 17 | border-color: var(--color-gray-200, currentColor); 18 | } 19 | } 20 | 21 | body { 22 | background-color: black; 23 | color: #ffffff; 24 | } 25 | -------------------------------------------------------------------------------- /day-10/pub-sub-pattern/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | 3 | /* 4 | The default border color has changed to `currentColor` in Tailwind CSS v4, 5 | so we've added these compatibility styles to make sure everything still 6 | looks the same as it did with Tailwind CSS v3. 7 | 8 | If we ever want to remove these styles, we need to add an explicit border 9 | color utility to any element that depends on these defaults. 10 | */ 11 | @layer base { 12 | *, 13 | ::after, 14 | ::before, 15 | ::backdrop, 16 | ::file-selector-button { 17 | border-color: var(--color-gray-200, currentColor); 18 | } 19 | } 20 | 21 | body { 22 | background-color: black; 23 | color: #ffffff; 24 | } 25 | -------------------------------------------------------------------------------- /day-02/controlled-uncontrolled-comp-pattern/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react/jsx-no-target-blank': 'off', 16 | 'react-refresh/only-export-components': [ 17 | 'warn', 18 | { allowConstantExport: true }, 19 | ], 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /day-08/optimistic-pattern/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | 3 | /* 4 | The default border color has changed to `currentColor` in Tailwind CSS v4, 5 | so we've added these compatibility styles to make sure everything still 6 | looks the same as it did with Tailwind CSS v3. 7 | 8 | If we ever want to remove these styles, we need to add an explicit border 9 | color utility to any element that depends on these defaults. 10 | */ 11 | @layer base { 12 | *, 13 | ::after, 14 | ::before, 15 | ::backdrop, 16 | ::file-selector-button { 17 | border-color: var(--color-gray-200, currentColor); 18 | } 19 | } 20 | 21 | body { 22 | background-color: black; 23 | color: #ffffff; 24 | } 25 | -------------------------------------------------------------------------------- /day-09/state-reducer-pattern/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | 3 | /* 4 | The default border color has changed to `currentColor` in Tailwind CSS v4, 5 | so we've added these compatibility styles to make sure everything still 6 | looks the same as it did with Tailwind CSS v3. 7 | 8 | If we ever want to remove these styles, we need to add an explicit border 9 | color utility to any element that depends on these defaults. 10 | */ 11 | @layer base { 12 | *, 13 | ::after, 14 | ::before, 15 | ::backdrop, 16 | ::file-selector-button { 17 | border-color: var(--color-gray-200, currentColor); 18 | } 19 | } 20 | 21 | body { 22 | background-color: black; 23 | color: #ffffff; 24 | } 25 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | 3 | /* 4 | The default border color has changed to `currentColor` in Tailwind CSS v4, 5 | so we've added these compatibility styles to make sure everything still 6 | looks the same as it did with Tailwind CSS v3. 7 | 8 | If we ever want to remove these styles, we need to add an explicit border 9 | color utility to any element that depends on these defaults. 10 | */ 11 | @layer base { 12 | *, 13 | ::after, 14 | ::before, 15 | ::backdrop, 16 | ::file-selector-button { 17 | border-color: var(--color-gray-200, currentColor); 18 | } 19 | } 20 | 21 | body { 22 | background-color: black; 23 | color: #ffffff; 24 | } 25 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/memoization/use-memo/Users.jsx: -------------------------------------------------------------------------------- 1 | import { useMemo } from "react"; 2 | 3 | function Users({ list }) { 4 | console.log("Users component rendered"); 5 | 6 | // const sorted = list.sort((a, b) => a.localeCompare(b)); 7 | 8 | const sorted = useMemo(() => { 9 | console.log("Sorting expensive list…"); 10 | return [...list].sort((a, b) => a.localeCompare(b)); 11 | }, [list]); 12 | 13 | return ( 14 | <> 15 |

Sorted Users

16 | {sorted.map((user, index) => ( 17 |
{user}
18 | ))} 19 | 20 | ); 21 | } 22 | 23 | export default Users; 24 | -------------------------------------------------------------------------------- /day-03/compound-components-patterns/src/with-pattern/accordion/Accordion.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | function Accordion({ children }) { 4 | return
{children}
; 5 | } 6 | 7 | function AccordionItem({ title, children }) { 8 | const [isOpen, setIsOpen] = useState(false); 9 | 10 | return ( 11 |
12 | 15 | {isOpen &&
{children}
} 16 |
17 | ); 18 | } 19 | 20 | // Attach subcomponents 21 | Accordion.Item = AccordionItem; 22 | 23 | export default Accordion; -------------------------------------------------------------------------------- /day-10/pub-sub-pattern/src/components/subscriber/CartBadge.jsx: -------------------------------------------------------------------------------- 1 | 2 | import { useState } from "react"; 3 | import { useEvent } from "../../hooks/useEvent"; 4 | 5 | export default function CartBadge() { 6 | const [items, setItems] = useState([]); 7 | 8 | useEvent("cart:add", (data) => { 9 | console.log(data) 10 | setItems([...items, data]); 11 | }); 12 | 13 | return (
14 |

Subscriber

15 |

🛒 {items.length}

16 |
    17 | {items.map(item => ( 18 |
  • {item.name}
  • 19 | ))} 20 |
21 |
); 22 | } -------------------------------------------------------------------------------- /day-08/optimistic-pattern/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react/jsx-no-target-blank': 'off', 16 | "react/prop-types": "off", 17 | 'react-refresh/only-export-components': [ 18 | 'warn', 19 | { allowConstantExport: true }, 20 | ], 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /day-10/pub-sub-pattern/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react/jsx-no-target-blank': 'off', 16 | "react/prop-types": "off", 17 | 'react-refresh/only-export-components': [ 18 | 'warn', 19 | { allowConstantExport: true }, 20 | ], 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /day-04/render-props-pattern/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react/jsx-no-target-blank': 'off', 16 | 'react/prop-types': 'off', 17 | 'react-refresh/only-export-components': [ 18 | 'warn', 19 | { allowConstantExport: true }, 20 | ], 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /day-09/state-reducer-pattern/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react/jsx-no-target-blank': 'off', 16 | "react/prop-types": "off", 17 | 'react-refresh/only-export-components': [ 18 | 'warn', 19 | { allowConstantExport: true }, 20 | ], 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /day-11/performance-patterns/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react/jsx-no-target-blank': 'off', 16 | "react/prop-types": "off", 17 | 'react-refresh/only-export-components': [ 18 | 'warn', 19 | { allowConstantExport: true }, 20 | ], 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /day-01/container-presenter-pattern/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react/jsx-no-target-blank': 'off', 16 | 'react/prop-types': 'off', 17 | 'react-refresh/only-export-components': [ 18 | 'warn', 19 | { allowConstantExport: true }, 20 | ], 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /day-03/compound-components-patterns/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react/jsx-no-target-blank': 'off', 16 | 'react/prop-types': 'off', 17 | 'react-refresh/only-export-components': [ 18 | 'warn', 19 | { allowConstantExport: true }, 20 | ], 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /day-01/container-presenter-pattern/src/with-pattern/components/post/PostsList.jsx: -------------------------------------------------------------------------------- 1 | const PostsList = ({ posts }) => { 2 | return ( 3 |
4 |

Recent Posts ({posts.length})

5 | {posts.length === 0 ? ( 6 |

No posts yet.

7 | ) : ( 8 | posts.map((post) => ( 9 |
10 |

{post.title}

11 |

{post.content.substring(0, 150)}...

12 | 13 | {new Date(post.createdAt).toLocaleDateString()} 14 | 15 |
16 | )) 17 | )} 18 |
19 | ); 20 | }; 21 | 22 | export default PostsList; 23 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/lazy-loading/NonLazyLoader.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import Light from "./Light"; 3 | import Heavy from "./Heavy"; 4 | 5 | 6 | const NonLazyLoader = () => { 7 | const [show, setShow] = useState(false); 8 | 9 | return ( 10 |
11 |

Non-Lazy Demo

12 |

Heavy component is bundled with the app (non-lazy).

13 | 14 | 15 | 16 | 19 | 20 | {show && } 21 |
22 | ); 23 | } 24 | 25 | export default NonLazyLoader -------------------------------------------------------------------------------- /day-09/state-reducer-pattern/src/App.jsx: -------------------------------------------------------------------------------- 1 | import FormFields from "./components/FormFields"; 2 | import Toggle from "./components/Toggle"; 3 | import { FormProvider } from "./provider/FormProvider"; 4 | import { customFormReducer } from "./reducers/form-reducer"; 5 | import { customToggleReducer } from "./reducers/toggle-reducer"; 6 | function App() { 7 | return ( 8 |
9 | console.log(v)} 12 | /> 13 | 14 | 15 | 16 |
17 | ); 18 | } 19 | 20 | export default App; 21 | -------------------------------------------------------------------------------- /day-05/hoc-pattern/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | "eslint:recommended", 6 | "plugin:react/recommended", 7 | "plugin:react/jsx-runtime", 8 | "plugin:react-hooks/recommended", 9 | ], 10 | ignorePatterns: ["dist", ".eslintrc.cjs"], 11 | parserOptions: { ecmaVersion: "latest", sourceType: "module" }, 12 | settings: { react: { version: "18.2" } }, 13 | plugins: ["react-refresh"], 14 | rules: { 15 | "react/jsx-no-target-blank": "off", 16 | "react/prop-types": "off", 17 | "react-refresh/only-export-components": [ 18 | "warn", 19 | { allowConstantExport: true }, 20 | ], 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /day-12/slot-pattern/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | "eslint:recommended", 6 | "plugin:react/recommended", 7 | "plugin:react/jsx-runtime", 8 | "plugin:react-hooks/recommended", 9 | ], 10 | ignorePatterns: ["dist", ".eslintrc.cjs"], 11 | parserOptions: { ecmaVersion: "latest", sourceType: "module" }, 12 | settings: { react: { version: "18.2" } }, 13 | plugins: ["react-refresh"], 14 | rules: { 15 | "react/jsx-no-target-blank": "off", 16 | "react/prop-types": "off", 17 | "react-refresh/only-export-components": [ 18 | "warn", 19 | { allowConstantExport: true }, 20 | ], 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /day-06/task.md: -------------------------------------------------------------------------------- 1 | # Day 06 - Your Task 2 | 3 | You’ve learned what Hooks are, how to write Custom Hooks, and how they make your components reusable and clean. 4 | Now it’s your turn to design and build 3 useful hooks like anyone can use in real-world apps. 5 | 6 | Create the following custom hooks from scratch, without relying on external libraries: 7 | 8 | - useLocalStorage(key, initialValue): Sync a React state with localStorage. 9 | - useClipboard(): Copy text to clipboard and show feedback. 10 | - useClickOutside(ref, callback): Detect clicks outside a given element. 11 | 12 | ## Criteria 13 | 14 | Each hook must: 15 | 16 | - Be reusable and follow the Rules of Hooks. 17 | - Include error handling or edge cases. 18 | - Be demonstrated in a small component that shows how to use it. 19 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/memoization/use-callback/ChildDemo.jsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useState } from "react"; 2 | import Child from "./Child"; 3 | const ChildDemo = () => { 4 | const [count, setCount] = useState(0); 5 | 6 | const handleClick = useCallback(() => { 7 | console.log("Child Clicked!"); 8 | }, []); 9 | 10 | console.log("App Rendered"); 11 | 12 | return ( 13 |
14 |

Count: {count}

15 |
16 | 17 | 18 | 19 |
20 |
21 | ); 22 | }; 23 | 24 | export default ChildDemo; 25 | -------------------------------------------------------------------------------- /day-12/task.md: -------------------------------------------------------------------------------- 1 | # Day 12 - Your Task 2 | 3 | ## Toolbar with responsive collapsing slots 4 | 5 | - Build a Toolbar component with three named slots: start, center, end. 6 | - On smaller screens, center content should move into an overflow menu (implement simple collapse behavior). 7 | - Deliverable: Toolbar.jsx, demo page showing responsiveness and slot usage. 8 | 9 | Learning: slot handling and responsive composition. 10 | 11 | ## Toast/Notification System with slot-based template 12 | 13 | - Implement a Toast system where each toast accepts slots: icon, message, action. 14 | - Provide an API to register toast templates and show toasts with different slot content. 15 | - Deliverable: toast manager , example templates (success, error, info). 16 | 17 | Learning: dynamic slot templates and programmatic rendering. 18 | -------------------------------------------------------------------------------- /day-05/hoc-pattern/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /day-09/state-reducer-pattern/src/components/Toggle.jsx: -------------------------------------------------------------------------------- 1 | import { useReducer } from "react"; 2 | import { toggleReducer } from "../reducers/toggle-reducer"; 3 | 4 | export default function Toggle({ reducer = toggleReducer, onToggle }) { 5 | const [state, dispatch] = useReducer(reducer, { on: false, clicks: 0 }); 6 | const { on, clicks } = state; 7 | 8 | function handleToggle() { 9 | dispatch({ type: "toggle" }); 10 | onToggle?.(!on); 11 | } 12 | 13 | return ( 14 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /day-12/slot-pattern/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /day-07/provider-pattern/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /day-10/pub-sub-pattern/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /day-04/render-props-pattern/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /day-06/custom-pattern-hooks/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /day-08/optimistic-pattern/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /day-09/state-reducer-pattern/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/throttling/ScrollTracker.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { useThrottle } from "./hooks/useThrottle"; 3 | 4 | export default function ScrollTracker() { 5 | const [scrollY, setScrollY] = useState(0); 6 | const throttledY = useThrottle(scrollY, 3000); 7 | 8 | useEffect(() => { 9 | const handleScroll = () => { 10 | setScrollY(window.scrollY); 11 | }; 12 | 13 | window.addEventListener("scroll", handleScroll); 14 | return () => window.removeEventListener("scroll", handleScroll); 15 | }, []); 16 | 17 | return ( 18 |
19 |

Scroll Position (Throttled)

20 |

{throttledY}

21 |
22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/throttling/hooks/useThrottle.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from "react"; 2 | 3 | export function useThrottle(value, delay = 300) { 4 | const [throttledValue, setThrottledValue] = useState(value); 5 | const lastExecuted = useRef(Date.now()); 6 | 7 | useEffect(() => { 8 | const handler = setTimeout(() => { 9 | const now = Date.now(); 10 | 11 | if (now - lastExecuted.current >= delay) { 12 | console.log("Do DOM Manipulation"); 13 | setThrottledValue(value); 14 | lastExecuted.current = now; 15 | } 16 | }, delay - (Date.now() - lastExecuted.current)); 17 | 18 | return () => clearTimeout(handler); 19 | }, [value, delay]); 20 | 21 | return throttledValue; 22 | } 23 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/virtualization/VirtualList.jsx: -------------------------------------------------------------------------------- 1 | import { FixedSizeList as List } from "react-window"; 2 | import { Item } from "./Item"; 3 | 4 | export default function VirtualList({ 5 | users, 6 | height = 600, 7 | itemHeight = 80, 8 | width = "100%", 9 | }) { 10 | return ( 11 | 19 | {({ index, style, data }) => ( 20 | 21 | )} 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /day-02/controlled-uncontrolled-comp-pattern/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /day-06/custom-pattern-hooks/src/components/MovieList.jsx: -------------------------------------------------------------------------------- 1 | import { useFetch } from "../hooks/useFetch"; 2 | 3 | function MoviesList() { 4 | const { data, error, loading } = useFetch( 5 | "https://json-faker.onrender.com/movies" 6 | ); 7 | 8 | if (loading) return

Loading movies...

; 9 | if (error) return

Error: {error}

; 10 | 11 | return ( 12 |
13 |

🎬 Movies

14 |
    15 | {data?.movies.map((movie) => ( 16 |
  • 17 | {movie.title} — {movie.director} 18 |
  • 19 | ))} 20 |
21 |
22 | ); 23 | } 24 | 25 | export default MoviesList; 26 | -------------------------------------------------------------------------------- /day-06/custom-pattern-hooks/src/hooks/useFetch.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | 3 | export function useFetch(url) { 4 | const [data, setData] = useState(null); 5 | const [error, setError] = useState(null); 6 | const [loading, setLoading] = useState(false); 7 | 8 | useEffect(() => { 9 | if (!url) return; 10 | 11 | const fetchData = async () => { 12 | setLoading(true); 13 | try { 14 | const response = await fetch(url); 15 | if (!response.ok) throw new Error("Network response was not ok"); 16 | const result = await response.json(); 17 | setData(result); 18 | } catch (err) { 19 | setError(err.message); 20 | } finally { 21 | setLoading(false); 22 | } 23 | }; 24 | 25 | fetchData(); 26 | }, [url]); 27 | 28 | return { data, error, loading }; 29 | } -------------------------------------------------------------------------------- /day-11/performance-patterns/src/PartOne.jsx: -------------------------------------------------------------------------------- 1 | import RenderTrackerDemo from "./performance/rerenders/RenderTrackerDemo"; 2 | 3 | import MemoizedProfileTracker from "./performance/memoization/memo/MemoizedProfileTracker"; 4 | 5 | import ChildDemo from "./performance/memoization/use-callback/ChildDemo"; 6 | 7 | import UsersSortingDemo from "./performance/memoization/use-memo/UsersSortingDemo"; 8 | 9 | import SearchBox from "./performance/debouncing/SearchBox"; 10 | 11 | import ScrollTracker from "./performance/throttling/ScrollTracker"; 12 | 13 | const PartOne = () => { 14 | return ( 15 | <> 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | ); 24 | }; 25 | 26 | export default PartOne; 27 | -------------------------------------------------------------------------------- /day-03/compound-components-patterns/src/with-pattern/accordion/AccordionDemo.jsx: -------------------------------------------------------------------------------- 1 | import Accordion from "./Accordion"; 2 | 3 | export default function AccordionDemo() { 4 | return ( 5 | 6 | 7 | It’s a React pattern that allows parent and child components to work 8 | together seamlessly while giving developers flexible composition. 9 | 10 | 11 | 12 | It makes UI libraries like modals, tabs, accordions, menus, etc. easier 13 | to build and use. 14 | 15 | 16 | 17 | Overusing it can lead to deeply nested structures or make things harder 18 | to debug if not documented well. 19 | 20 | 21 | ); 22 | } -------------------------------------------------------------------------------- /day-03/compound-components-patterns/src/App.css: -------------------------------------------------------------------------------- 1 | .modal-backdrop { 2 | position: fixed; 3 | inset: 0; 4 | background: rgba(0,0,0,0.5); 5 | display: flex; 6 | justify-content: center; 7 | align-items: center; 8 | } 9 | .modal-container { 10 | background: white; 11 | border-radius: 8px; 12 | padding: 1rem; 13 | width: 400px; 14 | position: relative; 15 | } 16 | .modal-header { font-weight: bold; margin-bottom: 1rem; } 17 | .modal-footer { display: flex; justify-content: flex-end; gap: 0.5rem; margin-top: 1rem; } 18 | .modal-close { position: absolute; top: 8px; right: 8px; background: none; border: none; font-size: 1.2rem; } 19 | 20 | .accordion-item { margin-bottom: 0.5rem; border: 1px solid #ddd; border-radius: 4px; } 21 | .accordion-title { width: 100%; text-align: left; padding: 0.5rem; font-weight: bold; cursor: pointer; background: #f9f9f9; border: none; } 22 | .accordion-content { padding: 0.5rem; background: #fff; } -------------------------------------------------------------------------------- /day-03/compound-components-patterns/src/with-pattern/modal/Modal.jsx: -------------------------------------------------------------------------------- 1 | 2 | 3 | function Modal({ children, isOpen, onClose }) { 4 | if (!isOpen) return null; 5 | 6 | return ( 7 |
8 |
9 | {children} 10 | 13 |
14 |
15 | ); 16 | } 17 | 18 | function ModalHeader({ children }) { 19 | return
{children}
; 20 | } 21 | 22 | function ModalBody({ children }) { 23 | return
{children}
; 24 | } 25 | 26 | function ModalFooter({ children }) { 27 | return
{children}
; 28 | } 29 | 30 | // Attach subcomponents 31 | Modal.Header = ModalHeader; 32 | Modal.Body = ModalBody; 33 | Modal.Footer = ModalFooter; 34 | 35 | export default Modal; 36 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/PartTwo.jsx: -------------------------------------------------------------------------------- 1 | import Dashboard from "./performance/component-isolation/Dashboard"; 2 | import IsolatedDashboard from "./performance/component-isolation/IsolatedDashboard"; 3 | import DeferredDemo from "./performance/deffered/DeferredDemo"; 4 | import LazyLoader from "./performance/lazy-loading/LazyLoader"; 5 | import NonLazyLoader from "./performance/lazy-loading/NonLazyLoader"; 6 | import TransitionDemo from "./performance/transition/TransitionDemo"; 7 | import VirtualizationDemo from "./performance/virtualization/VirtualizationDemo"; 8 | const PartTwo = () => { 9 | return ( 10 | <> 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ); 20 | }; 21 | 22 | export default PartTwo; 23 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/debouncing/SearchBox.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { useDebounce } from "./hooks/useDebounce"; 3 | 4 | export default function SearchBox() { 5 | const [query, setQuery] = useState(""); 6 | const debouncedQuery = useDebounce(query, 600); 7 | 8 | useEffect(() => { 9 | if (!debouncedQuery) return; 10 | 11 | console.log("API Call with:", debouncedQuery); 12 | 13 | // Simulated API: 14 | // fetch(`/api?q=${debouncedQuery}`) 15 | }, [debouncedQuery]); 16 | 17 | return ( 18 |
19 |

Search User

20 | setQuery(e.target.value)} 26 | /> 27 |
28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/virtualization/Item.jsx: -------------------------------------------------------------------------------- 1 | import { memo } from "react"; 2 | 3 | export const Item = memo(function Item({ index, style, data }) { 4 | const user = data[index]; 5 | 6 | console.log("Render row:", index); 7 | 8 | return ( 9 |
21 |
{user.name}
22 |
{user.email}
23 |
{user.bio}
24 |
25 | ); 26 | }); 27 | -------------------------------------------------------------------------------- /day-02/controlled-uncontrolled-comp-pattern/src/App.jsx: -------------------------------------------------------------------------------- 1 | import Counter from "./state-ref/components/Counter"; 2 | import AutoFocusInput from "./state-ref/components/AutoFocusInput"; 3 | import CounterWithRef from "./state-ref/components/CounterWithRef"; 4 | 5 | import FeedbackForm from "./messy/FeedbackForm"; 6 | 7 | import ControlledFeedbackForm from "./controlled/ControlledFeedbackForm"; 8 | 9 | import UncontrolledFeedbackForm from "./uncontrolled/UncontrolledFeedbackForm"; 10 | 11 | import UncontrolledFormNoRef from "./uncontrolled/UncontrolledFormNoRef"; 12 | 13 | function App() { 14 | return ( 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | ); 29 | } 30 | 31 | export default App; 32 | -------------------------------------------------------------------------------- /day-05/hoc-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-in-react-19", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^19.2.0", 14 | "react-dom": "^19.2.0", 15 | "react-error-boundary": "^4.0.13" 16 | }, 17 | "devDependencies": { 18 | "@tailwindcss/postcss": "^4.0.0", 19 | "@types/react": "npm:types-react@rc", 20 | "@types/react-dom": "npm:types-react-dom@rc", 21 | "@vitejs/plugin-react": "^4.2.1", 22 | "eslint": "^8.57.0", 23 | "eslint-plugin-react": "^7.34.1", 24 | "eslint-plugin-react-hooks": "^4.6.0", 25 | "eslint-plugin-react-refresh": "^0.4.6", 26 | "postcss": "^8.4.38", 27 | "tailwindcss": "^4.0.0", 28 | "vite": "^5.2.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /day-10/pub-sub-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-in-react-19", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^19.2.0", 14 | "react-dom": "^19.2.0", 15 | "react-error-boundary": "^4.0.13" 16 | }, 17 | "devDependencies": { 18 | "@tailwindcss/postcss": "^4.0.0", 19 | "@types/react": "npm:types-react@rc", 20 | "@types/react-dom": "npm:types-react-dom@rc", 21 | "@vitejs/plugin-react": "^4.2.1", 22 | "eslint": "^8.57.0", 23 | "eslint-plugin-react": "^7.34.1", 24 | "eslint-plugin-react-hooks": "^4.6.0", 25 | "eslint-plugin-react-refresh": "^0.4.6", 26 | "postcss": "^8.4.38", 27 | "tailwindcss": "^4.0.0", 28 | "vite": "^5.2.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /day-12/slot-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-in-react-19", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^19.2.1", 14 | "react-dom": "^19.2.1", 15 | "react-error-boundary": "^4.0.13" 16 | }, 17 | "devDependencies": { 18 | "@tailwindcss/postcss": "^4.0.0", 19 | "@types/react": "npm:types-react@rc", 20 | "@types/react-dom": "npm:types-react-dom@rc", 21 | "@vitejs/plugin-react": "^4.2.1", 22 | "eslint": "^8.57.0", 23 | "eslint-plugin-react": "^7.34.1", 24 | "eslint-plugin-react-hooks": "^4.6.0", 25 | "eslint-plugin-react-refresh": "^0.4.6", 26 | "postcss": "^8.4.38", 27 | "tailwindcss": "^4.0.0", 28 | "vite": "^5.2.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /day-04/render-props-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-in-react-19", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^19.2.0", 14 | "react-dom": "^19.2.0", 15 | "react-error-boundary": "^4.0.13" 16 | }, 17 | "devDependencies": { 18 | "@tailwindcss/postcss": "^4.0.0", 19 | "@types/react": "npm:types-react@rc", 20 | "@types/react-dom": "npm:types-react-dom@rc", 21 | "@vitejs/plugin-react": "^4.2.1", 22 | "eslint": "^8.57.0", 23 | "eslint-plugin-react": "^7.34.1", 24 | "eslint-plugin-react-hooks": "^4.6.0", 25 | "eslint-plugin-react-refresh": "^0.4.6", 26 | "postcss": "^8.4.38", 27 | "tailwindcss": "^4.0.0", 28 | "vite": "^5.2.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /day-06/custom-pattern-hooks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-in-react-19", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^19.2.0", 14 | "react-dom": "^19.2.0", 15 | "react-error-boundary": "^4.0.13" 16 | }, 17 | "devDependencies": { 18 | "@tailwindcss/postcss": "^4.0.0", 19 | "@types/react": "npm:types-react@rc", 20 | "@types/react-dom": "npm:types-react-dom@rc", 21 | "@vitejs/plugin-react": "^4.2.1", 22 | "eslint": "^8.57.0", 23 | "eslint-plugin-react": "^7.34.1", 24 | "eslint-plugin-react-hooks": "^4.6.0", 25 | "eslint-plugin-react-refresh": "^0.4.6", 26 | "postcss": "^8.4.38", 27 | "tailwindcss": "^4.0.0", 28 | "vite": "^5.2.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /day-07/provider-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-in-react-19", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^19.2.0", 14 | "react-dom": "^19.2.0", 15 | "react-error-boundary": "^4.0.13" 16 | }, 17 | "devDependencies": { 18 | "@tailwindcss/postcss": "^4.0.0", 19 | "@types/react": "npm:types-react@rc", 20 | "@types/react-dom": "npm:types-react-dom@rc", 21 | "@vitejs/plugin-react": "^4.2.1", 22 | "eslint": "^8.57.0", 23 | "eslint-plugin-react": "^7.34.1", 24 | "eslint-plugin-react-hooks": "^4.6.0", 25 | "eslint-plugin-react-refresh": "^0.4.6", 26 | "postcss": "^8.4.38", 27 | "tailwindcss": "^4.0.0", 28 | "vite": "^5.2.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /day-08/optimistic-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-in-react-19", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^19.2.0", 14 | "react-dom": "^19.2.0", 15 | "react-error-boundary": "^4.0.13" 16 | }, 17 | "devDependencies": { 18 | "@tailwindcss/postcss": "^4.0.0", 19 | "@types/react": "npm:types-react@rc", 20 | "@types/react-dom": "npm:types-react-dom@rc", 21 | "@vitejs/plugin-react": "^4.2.1", 22 | "eslint": "^8.57.0", 23 | "eslint-plugin-react": "^7.34.1", 24 | "eslint-plugin-react-hooks": "^4.6.0", 25 | "eslint-plugin-react-refresh": "^0.4.6", 26 | "postcss": "^8.4.38", 27 | "tailwindcss": "^4.0.0", 28 | "vite": "^5.2.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /day-09/state-reducer-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-in-react-19", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^19.2.0", 14 | "react-dom": "^19.2.0", 15 | "react-error-boundary": "^4.0.13" 16 | }, 17 | "devDependencies": { 18 | "@tailwindcss/postcss": "^4.0.0", 19 | "@types/react": "npm:types-react@rc", 20 | "@types/react-dom": "npm:types-react-dom@rc", 21 | "@vitejs/plugin-react": "^4.2.1", 22 | "eslint": "^8.57.0", 23 | "eslint-plugin-react": "^7.34.1", 24 | "eslint-plugin-react-hooks": "^4.6.0", 25 | "eslint-plugin-react-refresh": "^0.4.6", 26 | "postcss": "^8.4.38", 27 | "tailwindcss": "^4.0.0", 28 | "vite": "^5.2.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /day-03/compound-components-patterns/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-in-react-19", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^19.1.0", 14 | "react-dom": "^19.1.0", 15 | "react-error-boundary": "^4.0.13" 16 | }, 17 | "devDependencies": { 18 | "@tailwindcss/postcss": "^4.0.0", 19 | "@types/react": "npm:types-react@rc", 20 | "@types/react-dom": "npm:types-react-dom@rc", 21 | "@vitejs/plugin-react": "^4.2.1", 22 | "eslint": "^8.57.0", 23 | "eslint-plugin-react": "^7.34.1", 24 | "eslint-plugin-react-hooks": "^4.6.0", 25 | "eslint-plugin-react-refresh": "^0.4.6", 26 | "postcss": "^8.4.38", 27 | "tailwindcss": "^4.0.0", 28 | "vite": "^5.2.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /day-02/controlled-uncontrolled-comp-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-in-react-19", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^19.1.0", 14 | "react-dom": "^19.1.0", 15 | "react-error-boundary": "^4.0.13" 16 | }, 17 | "devDependencies": { 18 | "@tailwindcss/postcss": "^4.0.0", 19 | "@types/react": "npm:types-react@rc", 20 | "@types/react-dom": "npm:types-react-dom@rc", 21 | "@vitejs/plugin-react": "^4.2.1", 22 | "eslint": "^8.57.0", 23 | "eslint-plugin-react": "^7.34.1", 24 | "eslint-plugin-react-hooks": "^4.6.0", 25 | "eslint-plugin-react-refresh": "^0.4.6", 26 | "postcss": "^8.4.38", 27 | "tailwindcss": "^4.0.0", 28 | "vite": "^5.2.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/virtualization/NonVirtualList.jsx: -------------------------------------------------------------------------------- 1 | export default function NonVirtualList({ users }) { 2 | return ( 3 |
10 | {users.map((user) => ( 11 | // We reuse Item visual, but each receives no style height optimization 12 |
16 |
{user.name}
17 |
18 | {user.email} 19 |
20 |
{user.bio}
21 |
22 | ))} 23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /day-01/container-presenter-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-in-react-19", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "axios": "^1.12.2", 14 | "react": "^19.1.0", 15 | "react-dom": "^19.1.0", 16 | "react-error-boundary": "^4.0.13" 17 | }, 18 | "devDependencies": { 19 | "@tailwindcss/postcss": "^4.0.0", 20 | "@types/react": "npm:types-react@rc", 21 | "@types/react-dom": "npm:types-react-dom@rc", 22 | "@vitejs/plugin-react": "^4.2.1", 23 | "eslint": "^8.57.0", 24 | "eslint-plugin-react": "^7.34.1", 25 | "eslint-plugin-react-hooks": "^4.6.0", 26 | "eslint-plugin-react-refresh": "^0.4.6", 27 | "postcss": "^8.4.38", 28 | "tailwindcss": "^4.0.0", 29 | "vite": "^5.2.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /day-11/performance-patterns/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-in-react-19", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^19.2.0", 14 | "react-dom": "^19.2.0", 15 | "react-error-boundary": "^4.0.13", 16 | "react-window": "1.8.x" 17 | }, 18 | "devDependencies": { 19 | "@tailwindcss/postcss": "^4.0.0", 20 | "@types/react": "npm:types-react@rc", 21 | "@types/react-dom": "npm:types-react-dom@rc", 22 | "@vitejs/plugin-react": "^4.2.1", 23 | "babel-plugin-react-compiler": "^1.0.0", 24 | "eslint": "^8.57.0", 25 | "eslint-plugin-react": "^7.34.1", 26 | "eslint-plugin-react-hooks": "^4.6.0", 27 | "eslint-plugin-react-refresh": "^0.4.6", 28 | "postcss": "^8.4.38", 29 | "tailwindcss": "^4.0.0", 30 | "vite": "^5.2.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /day-12/slot-pattern/src/compound/CardCompound.jsx: -------------------------------------------------------------------------------- 1 | export default function CardCompound({ children }) { 2 | return ( 3 |
4 | {children} 5 |
6 | ); 7 | } 8 | 9 | CardCompound.Header = function Header({ children }) { 10 | return ( 11 |
18 | {children} 19 |
20 | ); 21 | }; 22 | 23 | CardCompound.Body = function Body({ children }) { 24 | return
{children}
; 25 | }; 26 | 27 | CardCompound.Actions = function Actions({ children }) { 28 | return ( 29 |
36 | {children} 37 |
38 | ); 39 | }; 40 | -------------------------------------------------------------------------------- /day-09/state-reducer-pattern/src/components/FormFields.jsx: -------------------------------------------------------------------------------- 1 | 2 | import { useFormContext } from "../hooks/useFormContext"; 3 | 4 | export default function FormFields() { 5 | const { state, dispatch } = useFormContext(); 6 | 7 | const handleChange = (e) => { 8 | dispatch({ 9 | type: "CHANGE_FIELD", 10 | payload: { name: e.target.name, value: e.target.value }, 11 | }); 12 | }; 13 | 14 | return ( 15 |
16 | 23 | 30 | 31 | {Object.entries(state.errors).map(([field, msg]) => ( 32 |

33 | {msg} 34 |

35 | ))} 36 |
37 | ); 38 | } -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/lazy-loading/LazyLoader.jsx: -------------------------------------------------------------------------------- 1 | import React, { Suspense, useState } from "react"; 2 | import Light from "./Light"; 3 | 4 | const Heavy = React.lazy(() => import("./Heavy")); 5 | 6 | const LazyLoader = () => { 7 | const [show, setShow] = useState(false); 8 | 9 | return ( 10 |
11 |

Lazy Demo

12 |

13 | Heavy component is loaded on demand via{" "} 14 | React.lazy(). 15 |

16 | 17 | 18 | 19 | 22 | 23 | Loading heavy component…
26 | } 27 | > 28 | {show && } 29 | 30 | 31 | ); 32 | }; 33 | 34 | export default LazyLoader; 35 | -------------------------------------------------------------------------------- /day-07/provider-pattern/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useTheme } from "./hook/useTheme"; 2 | import { useBrand } from "./hook/useBrand"; 3 | 4 | function App() { 5 | const { theme, toggleTheme } = useTheme(); 6 | const brand = useBrand(); 7 | 8 | console.log(brand); 9 | 10 | return ( 11 |
18 | 22 |
23 |

24 | {theme ? "☀️ Light Mode" : "🌙 Dark Mode"} 25 |

26 |
27 | {brand &&

{brand.name}

} 28 |
29 |
30 |
31 | ); 32 | } 33 | 34 | export default App; 35 | -------------------------------------------------------------------------------- /day-10/pub-sub-pattern/src/components/publisher/AddToCartButton.jsx: -------------------------------------------------------------------------------- 1 | import { eventBus } from "../../lib/eventBus"; 2 | 3 | const products = [ 4 | { id: crypto.randomUUID(), name: "Soap" }, 5 | { id: crypto.randomUUID(), name: "Towel" }, 6 | { id: crypto.randomUUID(), name: "Bed" }, 7 | { id: crypto.randomUUID(), name: "Mirror" }, 8 | { id: crypto.randomUUID(), name: "Light" }, 9 | ]; 10 | 11 | export default function AddToCartButton() { 12 | const handleClick = () => { 13 | const randomIndex = Math.floor(Math.random() * products.length); 14 | const selectedProduct = products[randomIndex]; 15 | eventBus.publish("cart:add", { 16 | id: selectedProduct.id, 17 | name: selectedProduct.name, 18 | }); 19 | }; 20 | 21 | return ( 22 |
23 |

Publisher

24 | 30 |
31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/derived/MoreExamples.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useMemo, useState } from "react"; 2 | 3 | export function Users({ list }) { 4 | const activeUsers = useMemo(() => { 5 | console.log("Filtering expensive list…"); 6 | return list.filter((user) => user.active); 7 | }, [list]); 8 | 9 | return ( 10 | <> 11 |

Active Users: {activeUsers.length}

12 | {activeUsers.map((user) => ( 13 |
{user.name}
14 | ))} 15 | 16 | ); 17 | } 18 | 19 | // Wrong - Boolean 20 | export function Actor({ age }) { 21 | const [isAdult, setIsAdult] = useState(false); 22 | 23 | useEffect(() => { 24 | setIsAdult(age >= 18); 25 | }, [age]); 26 | 27 | return <>{isAdult ?

Adult

:

Minor

}; 28 | } 29 | 30 | // Wrong - Form 31 | 32 | export function Form({ name, email }) { 33 | const [isValid, setIsValid] = useState(false); 34 | 35 | useEffect(() => { 36 | setIsValid(name !== "" && email.includes("@")); 37 | }, [name, email]); 38 | 39 | return <>{isValid ?

All Good

:

Bad!!

}; 40 | } 41 | -------------------------------------------------------------------------------- /day-11/performance-patterns/src/performance/lazy-loading/Heavy.jsx: -------------------------------------------------------------------------------- 1 | import { useMemo } from "react"; 2 | 3 | console.log("[Heavy module] module evaluated"); // important: shows when module is parsed/loaded 4 | 5 | function generateBigData(count = 200000) { 6 | // simulate heavy CPU work and big data (but keep it synchronous for demonstration) 7 | const arr = new Array(count); 8 | for (let i = 0; i < count; i++) { 9 | arr[i] = `user-${i}-${Math.random().toString(36).slice(2, 9)}`; 10 | } 11 | return arr; 12 | } 13 | 14 | export default function Heavy() { 15 | // useMemo to avoid regenerating every render, but module eval is the heavy part 16 | const data = useMemo(() => { 17 | console.log("[Heavy] generating big data (expensive)"); 18 | return generateBigData(200000); // adjust number for your machine 19 | }, []); 20 | 21 | return ( 22 |
23 |

Heavy Component

24 |

Loaded heavy data length: {data.length}

25 | 26 | Check console: module evaluated and data generation logs. 27 | 28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /day-12/README.md: -------------------------------------------------------------------------------- 1 | # Day 12 - Slot Pattern 2 | 3 | ## **🎯 Goal of This Lesson** 4 | 5 | - Day 12 6 | - Slots 7 | - Single Default Slot 8 | - Named Slots 9 | - Names Slot Map 10 | - Compound Components vs Slot patterns 11 | - Tasks 12 | - Surprise 13 | 14 | ## 🫶 Support 15 | 16 | Your support means a lot. 17 | 18 | - Please SUBSCRIBE to [tapaScript YouTube Channel](https://youtube.com/tapasadhikary) if not done already. A Big Thank You! 19 | - Liked my work? It takes months of hard work to create quality content and present it to you. You can show your support to me with a STAR(⭐) to this repository. 20 | 21 | > Many Thanks to all the `Stargazers` who have supported this project with stars(⭐) 22 | 23 | ### 🤝 Sponsor My Work 24 | 25 | I am an independent educator and open-source enthusiast who creates meaningful projects to teach programming on my YouTube Channel. **You can support my work by [Sponsoring me on GitHub](https://github.com/sponsors/atapas) or [Buy Me a Cofee](https://buymeacoffee.com/tapasadhikary)**. 26 | 27 | ## Video Sessions 28 | 29 | Here is the video for you to go through and learn: 30 | 31 | ### Video 32 | 33 | [![day-12](./banner.jpg)](https://youtu.be/_LBgDy0j-Os "Video") 34 | -------------------------------------------------------------------------------- /day-02/controlled-uncontrolled-comp-pattern/src/state-ref/components/CounterWithRef.jsx: -------------------------------------------------------------------------------- 1 | import { useRef, useState } from "react"; 2 | 3 | function CounterWithRef() { 4 | const countRef = useRef(0); // persists between renders 5 | const [renderCount, setRenderCount] = useState(0); // for forcing re-renders 6 | 7 | const increment = () => { 8 | countRef.current = countRef.current + 1; // update ref 9 | console.log("Ref Count:", countRef.current); 10 | }; 11 | 12 | return ( 13 |
14 |
15 |

Ref Count: {countRef.current}

16 | 17 |
18 | 19 |
20 |

Render Count: {renderCount}

21 | 24 |
25 |
26 | ); 27 | } 28 | 29 | export default CounterWithRef; 30 | -------------------------------------------------------------------------------- /day-02/controlled-uncontrolled-comp-pattern/src/uncontrolled/UncontrolledFormNoRef.jsx: -------------------------------------------------------------------------------- 1 | export default function UncontrolledFormNoRef() { 2 | const handleSubmit = (e) => { 3 | e.preventDefault(); 4 | 5 | // Use FormData API to grab values directly 6 | const formData = new FormData(e.target); 7 | const data = Object.fromEntries(formData.entries()); 8 | 9 | console.log("Form Data:", data); 10 | alert(`Hello ${data.username}, your email is ${data.email}`); 11 | }; 12 | 13 | return ( 14 |
15 | 20 | 26 | 32 |
33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /day-08/README.md: -------------------------------------------------------------------------------- 1 | # Day 08 - Optimistic Hook Pattern 2 | 3 | ## **🎯 Goal of This Lesson** 4 | 5 | - Hey, Optimistic 6 | - Things to Cover 7 | - What is Optimistic UI? 8 | - useOptimisitc Hook in React 9 | - Mini Project using useOptimistic() Hook 10 | - Error & Rollback 11 | - Use Cases & Ideas 12 | - Pifalls 13 | - Tasks 14 | 15 | ## 🫶 Support 16 | 17 | Your support means a lot. 18 | 19 | - Please SUBSCRIBE to [tapaScript YouTube Channel](https://youtube.com/tapasadhikary) if not done already. A Big Thank You! 20 | - Liked my work? It takes months of hard work to create quality content and present it to you. You can show your support to me with a STAR(⭐) to this repository. 21 | 22 | > Many Thanks to all the `Stargazers` who have supported this project with stars(⭐) 23 | 24 | ### 🤝 Sponsor My Work 25 | 26 | I am an independent educator and open-source enthusiast who creates meaningful projects to teach programming on my YouTube Channel. **You can support my work by [Sponsoring me on GitHub](https://github.com/sponsors/atapas) or [Buy Me a Cofee](https://buymeacoffee.com/tapasadhikary)**. 27 | 28 | ## Video 29 | 30 | Here is the video for you to go through and learn: 31 | 32 | [![day-08](./banner.jpg)](https://youtu.be/x03yX-yNxas "Video") 33 | -------------------------------------------------------------------------------- /day-05/README.md: -------------------------------------------------------------------------------- 1 | # Day 05 - HOC 2 | 3 | ## **🎯 Goal of This Lesson** 4 | 5 | - Day 05 6 | - We Will Learn 7 | - Higher Order Function(HOF) 8 | - Higher Order Component(HOC) 9 | - The Movie App 10 | - HOC in Code(Setup) 11 | - Coding Movie App With HOC 12 | - Use Cases 13 | - Pitfalls and Alternatives 14 | - Tasks and Wrapping Up 15 | 16 | ## 🫶 Support 17 | 18 | Your support means a lot. 19 | 20 | - Please SUBSCRIBE to [tapaScript YouTube Channel](https://youtube.com/tapasadhikary) if not done already. A Big Thank You! 21 | - Liked my work? It takes months of hard work to create quality content and present it to you. You can show your support to me with a STAR(⭐) to this repository. 22 | 23 | > Many Thanks to all the `Stargazers` who have supported this project with stars(⭐) 24 | 25 | ### 🤝 Sponsor My Work 26 | 27 | I am an independent educator and open-source enthusiast who creates meaningful projects to teach programming on my YouTube Channel. **You can support my work by [Sponsoring me on GitHub](https://github.com/sponsors/atapas) or [Buy Me a Cofee](https://buymeacoffee.com/tapasadhikary)**. 28 | 29 | ## Video 30 | 31 | Here is the video for you to go through and learn: 32 | 33 | [![day-05](./banner.jpg)](https://youtu.be/spDQ4oCKSPY "Video") 34 | -------------------------------------------------------------------------------- /day-09/README.md: -------------------------------------------------------------------------------- 1 | # Day 09 - State Reducer Pattern 2 | 3 | ## **🎯 Goal of This Lesson** 4 | 5 | - The State 6 | - State & Reducer in React 7 | - The useReducer() Hook 8 | - The State-Reducer Pattern 9 | - Implementing State-Reducer Pattern 10 | - State-Reducer-Provider-Context-Hook 11 | - Use Cases 12 | - Pitfalls 13 | - Tasks 14 | 15 | ## 🫶 Support 16 | 17 | Your support means a lot. 18 | 19 | - Please SUBSCRIBE to [tapaScript YouTube Channel](https://youtube.com/tapasadhikary) if not done already. A Big Thank You! 20 | - Liked my work? It takes months of hard work to create quality content and present it to you. You can show your support to me with a STAR(⭐) to this repository. 21 | 22 | > Many Thanks to all the `Stargazers` who have supported this project with stars(⭐) 23 | 24 | ### 🤝 Sponsor My Work 25 | 26 | I am an independent educator and open-source enthusiast who creates meaningful projects to teach programming on my YouTube Channel. **You can support my work by [Sponsoring me on GitHub](https://github.com/sponsors/atapas) or [Buy Me a Cofee](https://buymeacoffee.com/tapasadhikary)**. 27 | 28 | ## Video 29 | 30 | Here is the video for you to go through and learn: 31 | 32 | [![day-09](./banner.jpg)](https://youtu.be/vZswuz3NTgs "Video") 33 | -------------------------------------------------------------------------------- /day-00/README.md: -------------------------------------------------------------------------------- 1 | # Day 00 - 15 Days of React Design Patterns 2 | 3 | ## **🎯 Goal of This Lesson** 4 | 5 | - Welcome to the "15 Days of React Design Patterns" series! 6 | - The Problem 7 | - The Objective 8 | - React Refresher 9 | - Why Design Patterns? 10 | - 15 React Design Patterns 11 | - What’s Next! 12 | 13 | ## 🫶 Support 14 | 15 | Your support means a lot. 16 | 17 | - Please SUBSCRIBE to [tapaScript YouTube Channel](https://youtube.com/tapasadhikary) if not done already. A Big Thank You! 18 | - Liked my work? It takes months of hard work to create quality content and present it to you. You can show your support to me with a STAR(⭐) to this repository. 19 | 20 | > Many Thanks to all the `Stargazers` who have supported this project with stars(⭐) 21 | 22 | ### 🤝 Sponsor My Work 23 | 24 | I am an independent educator and open-source enthusiast who creates meaningful projects to teach programming on my YouTube Channel. **You can support my work by [Sponsoring me on GitHub](https://github.com/sponsors/atapas) or [Buy Me a Cofee](https://buymeacoffee.com/tapasadhikary)**. 25 | 26 | ## Video 27 | 28 | Here is the video for you to go through and learn: 29 | 30 | [![day-00](./banner.jpg)](https://www.youtube.com/watch?v=OWi31QoHqNk&pp=0gcJCckJAYcqIYzv "Video") 31 | -------------------------------------------------------------------------------- /day-06/README.md: -------------------------------------------------------------------------------- 1 | # Day 06 - Custom Hooks Pattern 2 | 3 | ## **🎯 Goal of This Lesson** 4 | 5 | - Day 06 6 | - What Will We Learn? 7 | - Why Hooks? 8 | - What is a Hook in React? 9 | - Built-in Hooks 10 | - Rules of Hooks 11 | - Custom Hooks 12 | - Writing Custom Hooks 13 | - Use Cases 14 | - Pitfalls 15 | - Tasks and Wrapping Up 16 | 17 | ## 🫶 Support 18 | 19 | Your support means a lot. 20 | 21 | - Please SUBSCRIBE to [tapaScript YouTube Channel](https://youtube.com/tapasadhikary) if not done already. A Big Thank You! 22 | - Liked my work? It takes months of hard work to create quality content and present it to you. You can show your support to me with a STAR(⭐) to this repository. 23 | 24 | > Many Thanks to all the `Stargazers` who have supported this project with stars(⭐) 25 | 26 | ### 🤝 Sponsor My Work 27 | 28 | I am an independent educator and open-source enthusiast who creates meaningful projects to teach programming on my YouTube Channel. **You can support my work by [Sponsoring me on GitHub](https://github.com/sponsors/atapas) or [Buy Me a Cofee](https://buymeacoffee.com/tapasadhikary)**. 29 | 30 | ## Video 31 | 32 | Here is the video for you to go through and learn: 33 | 34 | [![day-06](./banner.jpg)](https://www.youtube.com/watch?v=fhwvqTry_Vg "Video") 35 | -------------------------------------------------------------------------------- /day-02/controlled-uncontrolled-comp-pattern/src/uncontrolled/UncontrolledFeedbackForm.jsx: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | 3 | export default function UncontrolledFeedbackForm() { 4 | const nameRef = useRef(); 5 | const emailRef = useRef(); 6 | const messageRef = useRef(); 7 | 8 | const handleSubmit = (e) => { 9 | e.preventDefault(); 10 | const name = nameRef.current.value; 11 | const email = emailRef.current.value; 12 | const message = messageRef.current.value; 13 | 14 | if (!name) { nameRef.current.focus(); return; } 15 | if (!email.includes("@")) { emailRef.current.focus(); return; } 16 | if (!message) { messageRef.current.focus(); return; } 17 | 18 | console.log("Form submitted:", { name, email, message }); 19 | }; 20 | 21 | return ( 22 |
23 | 24 | 25 |