This shows you the differences between two versions of the page.
|
se:labs:03 [2025/10/19 18:43] ilie_cristian.sandu [Passing data from child to parent component] |
se:labs:03 [2025/10/19 20:34] (current) ilie_cristian.sandu [Tasks] |
||
|---|---|---|---|
| Line 190: | Line 190: | ||
| } | } | ||
| </code> | </code> | ||
| + | |||
| + | ==== (Optional) Other Useful React Hooks ==== | ||
| + | |||
| + | While `useState()` is the most common React hook, React also provides several others that help you manage side effects, access DOM elements, and optimize performance. | ||
| + | |||
| + | === useEffect === | ||
| + | |||
| + | The `useEffect()` hook lets you perform **side effects** in your components, such as fetching data, updating the document title, or setting up timers. | ||
| + | By default, it runs after every render, but you can control when it runs by specifying dependencies. | ||
| + | |||
| + | <code javascript> | ||
| + | import React, { useState, useEffect } from "react"; | ||
| + | |||
| + | export default function Timer() { | ||
| + | const [seconds, setSeconds] = useState<number>(0); | ||
| + | |||
| + | useEffect(() => { | ||
| + | const interval = setInterval(() => { | ||
| + | setSeconds((s) => s + 1); | ||
| + | }, 1000); | ||
| + | |||
| + | // Cleanup when component unmounts | ||
| + | return () => clearInterval(interval); | ||
| + | }, []); | ||
| + | |||
| + | return <h1>Seconds passed: {seconds}</h1>; | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | **Tip:** | ||
| + | * <code javascript> useEffect(() => {...}, []) // → runs only once (on mount) </code> | ||
| + | * <code javascript> useEffect(() => {...}, [value]) // → runs whenever `value` changes </code> | ||
| + | |||
| + | |||
| + | === useRef === | ||
| + | |||
| + | The `useRef()` hook gives you a **mutable reference** that persists across renders without causing re-renders. | ||
| + | It’s useful for accessing DOM elements or storing values like timers. | ||
| + | |||
| + | <code javascript> | ||
| + | import React, { useRef, useEffect } from "react"; | ||
| + | |||
| + | export default function FocusInput() { | ||
| + | const inputRef = useRef<HTMLInputElement>(null); | ||
| + | |||
| + | useEffect(() => { | ||
| + | inputRef.current?.focus(); | ||
| + | }, []); | ||
| + | |||
| + | return <input ref={inputRef} placeholder="Type here..." />; | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | |||
| + | === useContext === | ||
| + | |||
| + | The `useContext()` hook allows you to share values (like theme, language, or user info) across the component tree **without prop drilling**. | ||
| + | |||
| + | <code javascript> | ||
| + | import React, { createContext, useContext } from "react"; | ||
| + | |||
| + | const ThemeContext = createContext("light"); | ||
| + | |||
| + | function ThemeDisplay() { | ||
| + | const theme = useContext(ThemeContext); | ||
| + | return <h1>Current theme: {theme}</h1>; | ||
| + | } | ||
| + | |||
| + | export default function App() { | ||
| + | return ( | ||
| + | <ThemeContext.Provider value="dark"> | ||
| + | <ThemeDisplay /> | ||
| + | </ThemeContext.Provider> | ||
| + | ); | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | |||
| + | === useMemo and useCallback === | ||
| + | |||
| + | These two hooks are used for **performance optimization**. | ||
| + | |||
| + | * `useMemo()` caches computed values to avoid re-calculating them. | ||
| + | * `useCallback()` caches function references to prevent unnecessary re-renders. | ||
| + | |||
| + | <code javascript> | ||
| + | import React, { useState, useMemo, useCallback } from "react"; | ||
| + | |||
| + | export default function ExpensiveCalculation() { | ||
| + | const [count, setCount] = useState<number>(0); | ||
| + | const [input, setInput] = useState<number>(0); | ||
| + | |||
| + | const squared = useMemo(() => { | ||
| + | console.log("Calculating..."); | ||
| + | return input * input; | ||
| + | }, [input]); | ||
| + | |||
| + | const increment = useCallback(() => setCount((c) => c + 1), []); | ||
| + | |||
| + | return ( | ||
| + | <div> | ||
| + | <input | ||
| + | type="number" | ||
| + | value={input} | ||
| + | onChange={(e) => setInput(Number(e.target.value))} | ||
| + | /> | ||
| + | <p>Square: {squared}</p> | ||
| + | <button onClick={increment}>Count ({count})</button> | ||
| + | </div> | ||
| + | ); | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | |||
| + | === useReducer === | ||
| + | |||
| + | The `useReducer()` hook is an alternative to `useState()` for handling **more complex state logic**. | ||
| + | It works similarly to Redux reducers. | ||
| + | |||
| + | <code javascript> | ||
| + | import React, { useReducer } from "react"; | ||
| + | |||
| + | function reducer(state: number, action: string) { | ||
| + | switch (action) { | ||
| + | case "increment": | ||
| + | return state + 1; | ||
| + | case "decrement": | ||
| + | return state - 1; | ||
| + | default: | ||
| + | return state; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | export default function Counter() { | ||
| + | const [count, dispatch] = useReducer(reducer, 0); | ||
| + | |||
| + | return ( | ||
| + | <div> | ||
| + | <p>Count: {count}</p> | ||
| + | <button onClick={() => dispatch("increment")}>+</button> | ||
| + | <button onClick={() => dispatch("decrement")}>-</button> | ||
| + | </div> | ||
| + | ); | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | |||
| + | === Summary of Common Hooks === | ||
| + | |||
| + | ^ Hook ^ Purpose ^ | ||
| + | | `useState` | Manage local component state | | ||
| + | | `useEffect` | Handle side effects (fetching data, timers, etc.) | | ||
| + | | `useRef` | Persist values or access DOM elements | | ||
| + | | `useContext` | Share data across components | | ||
| + | | `useMemo` | Cache expensive calculations | | ||
| + | | `useCallback` | Cache function definitions | | ||
| + | | `useReducer` | Manage complex state logic | | ||
| ===== Data Binding ===== | ===== Data Binding ===== | ||
| Line 230: | Line 387: | ||
| }; | }; | ||
| - | interface ChildProps { | + | const Child = ({ chooseMessage }: { chooseMessage: (message: string) => void }) => { |
| - | onMessageSelect: (message: string) => void; | + | |
| - | } | + | |
| - | + | ||
| - | const Child = ({ onMessageSelect }: ChildProps) => { | + | |
| const msg = "Goodbye"; | const msg = "Goodbye"; | ||
| return ( | return ( | ||
| Line 247: | Line 400: | ||
| ===== Tailwind CSS ===== | ===== Tailwind CSS ===== | ||
| - | Tailwind CSS is a utility-first CSS framework that allows you to build custom designs quickly and efficiently. Unlike traditional CSS frameworks, which come with predefined components, Tailwind provides low-level utility classes for controlling layout, color, spacing, typography, and more, directly in your HTML. This approach gives you more flexibility without writing custom CSS. | + | Tailwind CSS is a **utility-first CSS framework** that allows you to build custom designs quickly and efficiently. |
| + | Unlike traditional CSS frameworks, which come with predefined components, Tailwind provides low-level utility classes for controlling layout, color, spacing, typography, and more — directly in your React components. | ||
| + | This approach gives you more flexibility without writing custom CSS. | ||
| Some key concepts of Tailwind CSS: | Some key concepts of Tailwind CSS: | ||
| - | * **Utility-First**: Tailwind provides small, single-purpose classes for styling (e.g., `**p-4**` for padding, `**text-center**` for text alignment). | + | * **Utility-First**: Tailwind provides small, single-purpose classes for styling (for example, `p-4` for padding or `text-center` for text alignment). |
| * **Responsive Design**: Tailwind has built-in support for responsive design, enabling you to style elements differently based on screen size. | * **Responsive Design**: Tailwind has built-in support for responsive design, enabling you to style elements differently based on screen size. | ||
| - | * **Customization**: You can customize or extend Tailwind's default configuration, adding your own color palettes, spacing, and more. | + | * **Customization**: You can customize or extend Tailwind's default configuration by adding your own color palettes, spacing, or typography. |
| - | Examples: | + | |
| **Centering Text** | **Centering Text** | ||
| - | To center-align text and add some padding, you can use the `**text-center**` and `**p-4**` utilities: | + | To center-align text and add some padding in React, you can use the `text-center` and `p-4` utilities with `className`: |
| - | <code html> | + | <code javascript> |
| - | <div class="text-center p-4"> | + | export default function CenteredText() { |
| - | <h1 class="text-xl font-bold">Hello, Tailwind CSS!</h1> | + | return ( |
| - | </div> | + | <div className="text-center p-4"> |
| + | <h1 className="text-xl font-bold">Hello, Tailwind CSS!</h1> | ||
| + | </div> | ||
| + | ); | ||
| + | } | ||
| </code> | </code> | ||
| **Responsive Layout** | **Responsive Layout** | ||
| - | With Tailwind, you can easily create responsive layouts using its responsive utilities. For instance, to set different column layouts on different screen sizes, you might do: | + | With Tailwind, you can easily create responsive layouts using its responsive utilities. |
| + | For instance, to set different column layouts on different screen sizes, you might do: | ||
| - | <code html> | + | <code javascript> |
| - | <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"> | + | export default function ResponsiveGrid() { |
| - | <div class="bg-blue-200 p-4">Item 1</div> | + | return ( |
| - | <div class="bg-blue-200 p-4">Item 2</div> | + | <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"> |
| - | <div class="bg-blue-200 p-4">Item 3</div> | + | <div className="bg-blue-200 p-4">Item 1</div> |
| - | <div class="bg-blue-200 p-4">Item 4</div> | + | <div className="bg-blue-200 p-4">Item 2</div> |
| - | </div> | + | <div className="bg-blue-200 p-4">Item 3</div> |
| + | <div className="bg-blue-200 p-4">Item 4</div> | ||
| + | </div> | ||
| + | ); | ||
| + | } | ||
| </code> | </code> | ||
| **Button Styling** | **Button Styling** | ||
| - | Tailwind allows you to create buttons with custom styles by combining utility classes: | + | Tailwind allows you to create buttons with custom styles by combining multiple utility classes. |
| + | In React, use `className` for the styling attribute: | ||
| - | <code html> | + | <code javascript> |
| - | <button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"> | + | export default function StyledButton() { |
| - | Click Me | + | return ( |
| - | </button> | + | <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"> |
| + | Click Me | ||
| + | </button> | ||
| + | ); | ||
| + | } | ||
| </code> | </code> | ||
| + | |||
| + | **More Examples** | ||
| + | |||
| + | Tailwind also provides utilities for: | ||
| + | * **Text colors** – `text-red-500`, `text-gray-700`, `text-green-600` | ||
| + | * **Backgrounds** – `bg-blue-100`, `bg-yellow-200` | ||
| + | * **Spacing** – `m-4`, `p-2`, `px-6` | ||
| + | * **Flexbox and grid layouts** – `flex`, `items-center`, `justify-between`, `grid-cols-3` | ||
| + | * **Typography** – `font-bold`, `text-lg`, `tracking-wide` | ||
| + | |||
| + | You can find the full list of utilities and examples in the official Tailwind CSS documentation: | ||
| + | **[[https://tailwindcss.com/docs|https://tailwindcss.com/docs]]** | ||
| + | |||
| + | ===== (Optional) Useful Resources for Modern React Development ===== | ||
| + | |||
| + | Once you understand the basics of React, there are several modern libraries and frameworks that can help you build professional, scalable applications more efficiently. | ||
| + | Below are some of the most widely used tools in the React ecosystem. | ||
| + | |||
| + | === ShadCN/UI === | ||
| + | |||
| + | [[https://ui.shadcn.com/|ShadCN/UI]] is a modern React component library built with **Tailwind CSS** and **Radix UI**. | ||
| + | It provides ready-to-use, accessible, and themable components (buttons, dialogs, data tables, forms, tooltips, etc.) that you can copy directly into your project and customize. | ||
| + | |||
| + | * Designed for flexibility — you own the code of each component. | ||
| + | * Uses Tailwind for styling. | ||
| + | * Fully compatible with Next.js and Vite setups. | ||
| + | |||
| + | **Example (Button component):** | ||
| + | |||
| + | <code javascript> | ||
| + | import { Button } from "@/components/ui/button"; | ||
| + | |||
| + | export default function Example() { | ||
| + | return <Button variant="outline">Click me</Button>; | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | === TanStack React Query === | ||
| + | |||
| + | [[https://tanstack.com/query/latest|TanStack Query (React Query)]] is a powerful library for **data fetching, caching, and synchronization** in React apps. | ||
| + | |||
| + | It simplifies working with APIs by managing loading states, caching results, refetching automatically, and handling background updates — all with minimal code. | ||
| + | |||
| + | **Example (fetching data):** | ||
| + | |||
| + | <code javascript> | ||
| + | import { useQuery } from "@tanstack/react-query"; | ||
| + | |||
| + | async function fetchUsers() { | ||
| + | const res = await fetch("https://jsonplaceholder.typicode.com/users"); | ||
| + | return res.json(); | ||
| + | } | ||
| + | |||
| + | export default function Users() { | ||
| + | const { data, isLoading, error } = useQuery({ | ||
| + | queryKey: ["users"], | ||
| + | queryFn: fetchUsers, | ||
| + | }); | ||
| + | |||
| + | if (isLoading) return <p>Loading...</p>; | ||
| + | if (error) return <p>Error loading data</p>; | ||
| + | |||
| + | return ( | ||
| + | <ul> | ||
| + | {data.map((user) => ( | ||
| + | <li key={user.id}>{user.name}</li> | ||
| + | ))} | ||
| + | </ul> | ||
| + | ); | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | === TanStack Table === | ||
| + | |||
| + | [[https://tanstack.com/table/latest|TanStack Table]] (formerly React Table) is a lightweight headless table library for building **interactive and customizable data tables**. | ||
| + | You control how the table is rendered — it only provides the logic for sorting, filtering, pagination, and selection. | ||
| + | |||
| + | **Example (basic table setup):** | ||
| + | |||
| + | <code javascript> | ||
| + | import { useReactTable, getCoreRowModel } from "@tanstack/react-table"; | ||
| + | |||
| + | const data = [ | ||
| + | { name: "Alice", age: 25 }, | ||
| + | { name: "Bob", age: 30 }, | ||
| + | ]; | ||
| + | |||
| + | const columns = [ | ||
| + | { header: "Name", accessorKey: "name" }, | ||
| + | { header: "Age", accessorKey: "age" }, | ||
| + | ]; | ||
| + | |||
| + | export default function BasicTable() { | ||
| + | const table = useReactTable({ | ||
| + | data, | ||
| + | columns, | ||
| + | getCoreRowModel: getCoreRowModel(), | ||
| + | }); | ||
| + | |||
| + | return ( | ||
| + | <table> | ||
| + | <thead> | ||
| + | {table.getHeaderGroups().map((headerGroup) => ( | ||
| + | <tr key={headerGroup.id}> | ||
| + | {headerGroup.headers.map((header) => ( | ||
| + | <th key={header.id}>{header.column.columnDef.header}</th> | ||
| + | ))} | ||
| + | </tr> | ||
| + | ))} | ||
| + | </thead> | ||
| + | <tbody> | ||
| + | {table.getRowModel().rows.map((row) => ( | ||
| + | <tr key={row.id}> | ||
| + | {row.getVisibleCells().map((cell) => ( | ||
| + | <td key={cell.id}>{cell.getValue()}</td> | ||
| + | ))} | ||
| + | </tr> | ||
| + | ))} | ||
| + | </tbody> | ||
| + | </table> | ||
| + | ); | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | === Other Notable Tools === | ||
| + | |||
| + | * **[[https://nextjs.org/|Next.js]]** – React framework for building full-stack apps with server-side rendering and routing. | ||
| + | * **[[https://react.dev/learn|React Docs]]** – The official React documentation (excellent for up-to-date examples). | ||
| + | * **[[https://tailwindcss.com/docs|Tailwind CSS Docs]]** – Learn how to customize design tokens, responsive utilities, and themes. | ||
| + | * **[[https://radix-ui.com/|Radix UI]]** – Low-level, accessible UI primitives used by ShadCN/UI. | ||
| + | * **[[https://reactrouter.com/en/main|React Router]]** – Client-side routing for React apps. | ||
| + | * **[[https://formik.org/|Formik]]** and **[[https://react-hook-form.com/|React Hook Form]]** – For building and validating forms easily. | ||
| + | |||
| + | ✅ **Tip:** | ||
| + | These libraries are not required for this lab, but they are very common in real-world React projects and can greatly improve developer productivity and user experience. | ||
| + | |||
| Line 292: | Line 597: | ||
| ====== Tasks ====== | ====== Tasks ====== | ||
| - | - Download the generated project {{:se:labs:se-lab3-tasks.zip|}} (and run `npm install` and `npm start`) | + | - Download the generated project {{:se:labs:se-lab3-tasks.zip|}} (and run **npm install** and **npm run dev**) |
| - Create a to do list app: | - Create a to do list app: | ||
| - | - Implement the List component in `components/ToDoList.tsx` | + | - Implement the List component in **components/ToDoList.tsx** |
| - | - Create a new component in `components/` that will represent __a todo-list item__ that should have: | + | - Create a new component in **components/** that will represent __a todo-list item__ that should have: |
| - text showing the description of the todo-list item | - text showing the description of the todo-list item | ||
| - edit button that enable you to edit todo-item text | - edit button that enable you to edit todo-item text | ||